首页 > 其他分享 >17. 面向对象的特征

17. 面向对象的特征

时间:2024-10-16 20:00:37浏览次数:6  
标签:name 17 特征 self 面向对象 print 父类 age def

一、面向对象的三大特征

  面向对象的三大特征指的是 封装继承多态

  封装(encapsulation,有时称为数据隐藏)是处理对象的一个重要概念。从形式上看,封装就是将数据和行为组合在一个包中,并对对象的使用者隐藏具体的实现方式。

  继承(inheritance)的基本思想是,可以基于已有的类创建新类。继承以存在的类就是复用(继承)这些类的方法,而且可以增加一些新的属性和方法,使新类能够适应新的情况。

  多态(polynirphic)就是一个类的多种形态。

二、封装性

2.1、什么是封装性

  所谓的封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或对象开放,向没必要开放的类或对象异常信息。封装性就是隐藏内部对象的复杂性,只对外公开简单的接口。便于外部调用,从而提高系统的可扩展性、可维护性。通俗的来说,把该隐藏的隐藏起来,该暴露的暴露出来。

  当我们创建一个类时,我们可以通过“对象.属性”的方式,对对象的属性进行赋值。此时,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没有其它制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,只能通过方法进行限制条件的添加。同时,我们需要避免用户在使用“对象.属性”的方式对属性进行赋值,则需要将属性声明为私有的(private)。此时,针对于属性就体现了封装性。

  使用封装后,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性。我们隐藏了属性名,使调用者无法任意修改对象中的属性。并且我们增加了 getter() 和 setter() 方法,可以很好的控制属性是否为只读的。如果希望属性是只读的,则可以直接去掉 setter() 方法,如果希望属性不能被外部访问,则可以直接去掉 getter() 方法。而且在使用 setter() 方法设置属性时,可以增加数据的验证,确保数据的值是正确的。在使用 getter() 方法获取属性,使用 setter() 方法设置属性时,可以在读取属性和修改属性的同时做一些其它的处理。

2.2、封装性的体现

  我们将类的属性(xxx)隐藏,同时,提供的公共的方法类获取(getter())和设置(setter(self))此属性的值。在 Python 中,我们可以为对象的属性使用双下划线开头 __xxx,双下划线开头的属性,是对象的隐藏属性,隐藏属性值只能在类的内部访问,无法通过对象访问。

class Person:
    def __init__(self,name,age):
        self.__name = name
        self.__age = age

    def get_name(self):
        return self.__name
  
    def set_name(self,name):
        self.__name = name

    def get_age(self):
        return self.__age
  
    def set_age(self,age):
        if age >= 0:
            self.__age = age

    def __show_info(self):
        print(f"name: {self.__name}, age: {self.__age}")

  如果我们直接访问类中的隐藏属性,会报以下错误:

p1 = Person("Sakura",10)
print(p1.__name)
AttributeError: 'Person' object has no attribute '__name'

  此时,我们可以通过公共的 getter() 方法和 setter() 方法调用类的私有属性:

p1 = Person("Sakura",10)
print("name: ", p1.get_name() , ", age: ", p1.get_age())

p1.set_age(12)
print("name: ", p1.get_name() , ", age: ", p1.get_age())

p1.set_age(-10)
print("name: ", p1.get_name() , ", age: ", p1.get_age())

  其实,隐藏属性是假隐藏,只不过是 Python 自动为属性改了一个名字。实际上,Python 将名字修改为 _类型__属性名

p1 = Person("Sakura",10)
p1._Person__age = 12
print(p1._Person__age)
p1._Person__show_info()

使用双下划线开头的属性,实际上依然可以在外部访问。在开发中,我们可以将一些私有属性以下划线开头(实际上是公开的属性),告诉开发人员没有特殊需要不要修改私有属性。

2.3、property装饰器

  property 装饰器,用来将一个 getter() 方法,转换为对象的属性,添加了 property 装饰器以后,我们就可以向调用属性一样使用 getter() 方法。使用 property 装饰的方法必须和属性名是一样的。setter() 方法的装饰器为:@属性名.setter;

class Person:
    def __init__(self,name):
        self.__name = name

    @property
    def name(self):
        print("get_name(self)方法执行了")
        return self.__name
  
    @name.setter
    def name(self,name):
        print("set_name(self,name)方法执行了")
        self.__name = name
  
    @name.deleter
    def name(self):
        print("del_name(self,name)方法执行了")

p = Person("Sakura")
print(p.name,end="\n\n")

p.name = "Mikoto"
print(p.name,end="\n\n")

del p.name

  在一些较老版本的 Python 解释器中,我们可以通过如下的方法实现。

class Person:
    def __init__(self,name):
        self.__name = name

    def get_name(self):
        print("get_name(self)方法执行了")
        return self.__name
  
    def set_name(self,name):
        print("set_name(self,name)方法执行了")
        self.__name = name
  
    def del_name(self):
        print("del_name(self,name)方法执行了")

    name = property(get_name, set_name, del_name)

p = Person("Sakura")
print(p.name,end="\n\n")

p.name = "Mikoto"
print(p.name,end="\n\n")

del p.name

三、继承性

3.1、什么是继承性

  继承,其基本思想就是基于某个父类的扩展,制定出一个新的子类,子类可以继承父类原有的属性和方法,也可以增加原来父类所不具备的属性和方法,或者直接重写父类中的某些方法。

  通过继承可以直接让子类获取父类的方法和属性,避免编写重复性的代码,并且也符合 OCP 原则。所以,我们经常需要通过继承来对一个类进行扩展。

3.2、继承性的应用

  使用继承后,我们可以减少代码的冗余,提高了代码的复用性,并且使用继承后,便于功能的拓展。继承的出现让类与类之间产生了 is-a 的关系。

  在 Python 中,继承性的格式如下:

class A(父类):
    pass

  其中,A 类代表 子类(派生类、subclass),B 类代表 父类(基类、superclass)。一旦 子类 A 继承 父类 B 之后,子类 A 中就获取 父类 B 中声明的所有的结构:属性、方法(包括特殊方法);子类继承父类以后,还可以声明自己特有的属性和方法,实现功能的拓展;

class Animal:
    def run(self):
        print("动物在跑")

    def sleep(self):
        print("动物在睡觉")

class Dog(Animal):
    def bark(self):
        print("汪汪汪")

dog = Dog()

dog.run()
dog.sleep()
dog.bark()
print()

# isinstance()检查一个对象是否是一个类的实例
# 如果这个类是这个对象的父类,也会返回true
print(isinstance(dog,Dog))
print(isinstance(dog,Animal))
print()

# issubclass()检查一个类是或否是另一个类的子类
print(issubclass(Dog,Animal))

如果在创建类时,省略了父类,则默认父类为 object,它是所有类的父类,所有类都继承 object;

3.3、方法重写

  如果在子类中有和父类同名的方法,则通过子类实例调用方法时,会调用子类的方法而不是父类的方法,这个特点我们成为 方法重写(覆盖,override)。

  当我们调用一个对象的方法时,它会优先去当前对象中寻找是否具有该方法,如果有则直接调用,如果没有则取当前对象的父类中寻找,如果父类中有则直接调用父类中的方法,如果没有则取父类中的父类中寻找,以此类推,直到找到 object,如果依然没有找到则报错。

class Animal:
    def run(self):
        print("动物在跑")

    def sleep(self):
        print("动物在睡觉")

class Dog(Animal):
    def run(self):
        print("狗在跑")

    def bark(self):
        print("汪汪汪")

dog = Dog()

dog.run()
dog.sleep()
dog.bark()

3.4、super()方法的使用

  父类中所有结构:属性 和 方法(包括特殊方法)都会被子类继承。如果我们希望直接调用父类中的结构,我们可以使用 super() 方法获取当前类的父类,并且通过 super() 调用父类方法时,不需要传递 self。

class Animal:
    def __init__(self,name):
        self.__name = name

    @property
    def name(self):
        return self.__name
  
    @name.setter
    def name(self,name):
        self.__name = name

class Dog(Animal):
    def __init__(self,name,age):
        # 直接调用父类中的__init__(self)来初始化父类中定义的属性
        super().__init__(name)
        self.__age = age

    @property
    def age(self):
        return self.__age
  
    @age.setter
    def age(self,age):
        self.__age = age

dog = Dog("旺财",3)
print("name: ", dog.name, ", age: ", dog.age)

3.5、多重继承

  在 Python 中是支持多重继承的,也就是我们可以为一个类同时指定多个父类。我们可以在类名的 () 后面添加多个类,来实现多重继承。多重继承会使子类同时拥有多个父类,并且或获取所有父类中的方法。如果多个父类中有同名的方法,则会先在第一个父类中寻找,然后找第二个,然后找第三个,...,前面父类的方法会覆盖后面父类的方法。

class A:
    def test(self):
        print("AAA")

class B:
    def test(self):
        print("BBB")

class C(A,B):
    pass

# 类名.__bases__这个属性可以获取当前类的所有父类
print(C.__bases__)
# 我们可以通过类.mro()查看属性的查找顺序
print(C.mro())

c = C()
c.test()

super() 在调用父类的时候,它需要计算出当前到底调用哪个父类,在 Python 中实现这个功能的算法叫 C3 算法;

  在 Python 中子类可以同时继承多个父类的属性,这样可以最大限度地重用代码,但是使用多继承会导致扩展性变差,并且有可能会导致棱形问题(钻石问题)。棱形问题(钻石问题)指的是一个子类继承的多个父类汇集到一个非 object 类的身上。
棱形问题

class A:
    def test(self):
        print("from A")

class B(A):
    def test(self):
        print("from B")

class C(A):
    def test(self):
        print("from C")

class D(B):
    def test(self):
        print("from D")

class E(C):
    def test(self):
        print("from E")

class F(A):
    def test(self):
        print("from F")

class G(D,E,F):
    pass

# 类名.__bases__这个属性可以获取当前类的所有父类
print(G.__bases__)
# 我们可以通过类.mro()查看属性的查找顺序
print(G.mro())
obj = G()
obj.test()

  如果真的涉及到一个子类不可避免使用多个父类的属性,应该使用 Mixins 机制。Mixin 类表示某一功能,而不是某个物品,Python 对于Mixin 类的命名方式一般以 Mixin,able,ible 为后缀。我们可以将相关的功能放在一个 Mixin 类中,如果有多个不同功能,那可以写多个 Mixin 类,一个类可以继承多个 Mixin 类,但应该只继承一个表示其归属含义的父类,以确保遵循继承的 "is-a" 原则。Mixin 类不应该依赖于子类的实现,子类即便没有继承这个 Mixin 类,也可以照常工作,只是缺少了某个功能而已。

class Vehicle:
    pass

class FlyerMixin:
    def fly(self):
        pass

class CivilAircraft(FlyerMixin,Vehicle):
    pass

class Helicopter(FlyerMixin,Vehicle):
    pass

class Car(Vehicle):
    pass

四、多态性

  多态性指的是一个事物的多种形态;同样的行为(函数),传入不同的对象,得到不同的状态;多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象。

  它的格式如下:

class A:
    def __init__(self,name):
        self.__name = name

    @property
    def name(self):
        return self.__name
  
    @name.setter
    def name(self,name):
        self.__name = name
  
class B:
    def __init__(self,name):
        self.__name = name

    @property
    def name(self):
        return self.__name
  
    @name.setter
    def name(self,name):
        self.__name = name

a = A("Sakura")
b = B("Mikoto")

# 对于say_hello()这个函数来说,只要对象中含有name属性,它就可以作为参数传递
# 这个函数并不会考虑对象的类型,只要有name属性即可
def say_hello(obj):
    print("你好,%s" %obj.name)

say_hello(a)
say_hello(b)
import abc

# 使用模块abc统一所有子类的标准
class Animal(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        print("汪汪汪!")

class Cat(Animal):
    def speak(self):
        print("喵喵喵!")

def make_noise(animal):
    animal.speak()

#animal = Animal()       # 不能实例化抽象类自己

dog = Dog()
cat = Cat()

make_noise(dog)
make_noise(cat)

看上去调用相同的方法,但实际上需要这个看这个对象是父类还是子类创建的对象,如果是父类创建的对象,一定调用父类中定义的方法,如果是子类创建的对象,那么就要看子类是否重写了父类的方法,如果子类要是重写了父类的方法,那么会调用子类的方法,如果子类没有重写父类方法,那么会调用父类的方法。

标签:name,17,特征,self,面向对象,print,父类,age,def
From: https://www.cnblogs.com/FlurryHeart/p/18470794

相关文章

  • 利用面向对象制作一个植物大战僵尸游戏(未完成)
    如何利用面向植物大战僵尸的游戏如何利用面向对象开始制作面向对象的技术流程可以简要概括为:需求模型(明确需求)——>邻域模型(基于需求模型,提炼出邻域相关的概念)——>设计模型(以领域模型为基础综合面向对象技巧完成类的设计)——>实现模型(以设计模型为基础翻译成具体的语言......
  • [题解]P3952 [NOIP2017 提高组] 时间复杂度
    P3952[NOIP2017提高组]时间复杂度我们把循环的嵌套关系看做树形结构,梳理一下\(3\)种情况:直接跳过当前子树:\(x,y\in\mathbb{N}\),且\(x>y\)。\(x=\tt{"n"},y\in\mathbb{N}\)。不跳过,并在处理完所有子节点后追加\(n\)的时间复杂度:\(x\in\mathbb{N},y=\tt{"n"}\)。......
  • SciTech-AV-Audio-DAP(Digital Audio Processing)-Perceived Loudness(感知响度)-EBU
    PerceivedLoudness(EBUR128)ThespecificationEBUR128/ITU-RBs.1770-4describesanalgorithmforcalculationoftheperceivedloudnessofrealworldaudiosignals,forexamplenopuresinetones.FordetailsoftherecommendationspleaseseeEBUR......
  • Educational Codeforces Round 170 (Rated for Div. 2)
    A.TwoScreens难点是读题,找到最长公共前缀即可。#include<bits/stdc++.h>usingnamespacestd;usingi32=int32_t;usingi64=longlong;#defineinti64usingvi=vector<int>;usingpii=pair<int,int>;consti32inf=INT_MAX/2;constintm......
  • 洛谷 P5175 数列 题解
    纯纯数学题。看到\(n\le10^{18}\)不难想到矩乘,但是\(\log_210^{18}\approx60\),再加上\(T=30000\)的多测,运算量已经来到了\(1.8\times10^6\),所以我们最多有一个\(\sqrt[3]{\frac{1.5\times10^8}{6\times10^6}}\approx4\)的矩阵。\[\becausea_i=xa_{i-1}+ya_{......
  • 极狐GitLab 发布安全补丁版本 17.4.2, 17.3.5, 17.2.9
    本分分享极狐GitLab补丁版本17.4.2,17.3.5,17.2.9的详细内容。今天,极狐GitLab专业技术团队正式发布了17.4.2,17.3.5,17.2.9版本。这几个版本包含重要的缺陷和安全修复代码,我们强烈建议所有私有化部署用户应该立即升级到上述的某一个版本。对于极狐GitLabSaaS,技术团队......
  • c++面向对象的两种格式
            面向对象编程(OOP)是C++的一个重要特性,它允许你将代码组织成类(class)和对象(object),从而提高代码的可读性、可维护性和复用性。所以,在项目开发中使用面向对象编程是非常重要的,即便函数也可以提高封装性,但是,类的使用通俗来说,直接将函数封装,同时可以通过继承父类来大......
  • Python实现K近邻算法:面向对象的思路与详细案例解析
    目录Python实现K近邻算法:面向对象的思路与详细案例解析引言一、K近邻算法的基本原理1.1K近邻算法的核心思想1.2距离度量1.3K的选择二、面向对象的KNN实现2.1类设计2.2Python代码实现2.3代码详解三、案例分析3.1案例一:鸢尾花分类问题描述数据准备模型训练与预测......
  • Python实现朴素贝叶斯算法:面向对象的思路与详细案例解析
    目录Python实现朴素贝叶斯算法:面向对象的思路与详细案例解析引言一、朴素贝叶斯算法的基本原理1.1贝叶斯定理1.2朴素假设二、面向对象的朴素贝叶斯实现2.1类设计2.2Python代码实现朴素贝叶斯分类器实现2.3代码详解三、案例分析3.1案例一:鸢尾花分类问题描述数据......
  • YOLOv11改进策略【卷积层】| ICCV-2023 SAFM 空间自适应特征调制模块 对C3k2进行二次
    一、本文介绍本文记录的是利用空间自适应特征调制模块SAFM优化YOLOv11的目标检测方法研究。SAFM通过更好地利用特征信息来实现模型性能和效率的平衡。本文通过二次创新C3k2,能够动态选择代表性特征,并结合局部上下文信息,提升模型的检测精度。专栏目录:YOLOv11改进目录一览......