面向对象三大特性:封装、继承、多态(最全面最详细的解释)
面向对象三大特性:封装、继承、多态封装概念:将一些属性和相关方法封装在一个对象中,对外隐藏内部具体实现细节。内部实现,外界不需要关心,外界只需要根据”内部提供的接口“去使用就可以。好处:使用起来更加方便:因为已经把很多相关的功能,封装成一个整体,类似于像外界提供一个工具箱,针对于不同的场景,使用不同的工具箱就可以;保证数据的安全:针对于安全级别高的数据,可以设置成”私有“,可以控制数据为只读(外界
面向对象的三大特性
面向对象三大特性:封装、继承、多态
封装
概念:
将一些属性和相关方法封装在一个对象中,对外隐藏内部具体实现细节。
内部实现,外界不需要关心,外界只需要根据”内部提供的接口“去使用就可以。
好处:
-
使用起来更加方便:因为已经把很多相关的功能,封装成一个整体,类似于像外界提供一个工具箱,针对于不同的场景,使用不同的工具箱就可以;
-
保证数据的安全:针对于安全级别高的数据,可以设置成”私有“,可以控制数据为只读(外界无法修改),也可以拦截数据的写操作(进行数据校验和过滤);
-
利于代码维护:如果后期,功能代码需要维护,则直接修改这个类内部代码即可;只要保证接口名称不变,外界不需要做任何代码修改。
继承
概念:
现实中的”继承“:子女继承父母的”财产资源“
编程中的”继承“:一个类”拥有“另外一个类的”资源“的方式之一;”拥有“:并不是资源的复制,编程双份资源,而是,资源的”使用权“;”资源“:指"非私有的"属性和方法。例如Dog类继承自Animal类
被继承的类:父类,基类,超类
继承的类:子类,派生类
目的:
方便资源重用
分类:
单继承:
-
概念:仅仅继承了一个父类。
-
class Father: pass class Chile(Father): pass
多继承:
-
概念:继承了多个父类
-
class Father: pass class Mother: pass class Child(Father, Mother): pass
查看父类的
Child.__bases__
type和object区分
继承下的影响:
资源的继承
明确:在Python中,继承是指资源的使用权,所以,测试某个资源能否被继承,其实就是测试在子类当中,能不能访问到父类当中的资源
结论:Python中的继承并不是复制,只是使用资源。子类除不能访问父类的私有的属性和私有的方法外,其余均可继承(公有属性/方法,受保护属性/方法,内置方法)。
class Animal:
# 属性和方法
# 设置不同权限的属性和方法,继承当中进行测试
# 在子类当中能否访问到这些资源
a = 1 # 公共属性
_b = 2 # 保护属性
__c = 3 # 私有属性
def t1(self): # 公共方法
print('t1')
def _t2(self): # 保护方法
print('t2')
def __t3(self): # 私有方法
print('t3')
def __init__(self): #内置
print("init, Animal")
class Person(Animal):
def test(self):
print(id(self.a))
print(self.a)
print(self._b)
# print(self.__c)
self.t1()
self._t2()
# self.__t3()
self.__init__()
p = Person()
p.test()
print(id(Animal.a))
Animal.a = 666
p.test()
资源的使用
- 继承的几种形态:
- 单继承链
- 无重叠的多继承链
- 有重叠的多继承链
- 几种形态应该遵循的规则
- 单继承链
- 从下往上(A.age 先从A自身查找,若无,B查找,若无,C查找)
A==>B==>C
- 从下往上(A.age 先从A自身查找,若无,B查找,若无,C查找)
- 无重叠的多继承链
- 单调原则,优先先到左侧查找。(A.age先从A自身查找,若无,B中查找,如无,D中查找,若无,C中查找,如无,E中查找)
A==>B==>D==>C==>E
- 单调原则,优先先到左侧查找。(A.age先从A自身查找,若无,B中查找,如无,D中查找,若无,C中查找,如无,E中查找)
- 有重叠的多继承链
- 从下到上,单调原则,优先上一级左,右,然后上上级。(A.age先从A自身查找,若无,B中查找,如无,C中查找,如无,D中查找)
A==>B==>C==>D
- 从下到上,单调原则,优先上一级左,右,然后上上级。(A.age先从A自身查找,若无,B中查找,如无,C中查找,如无,D中查找)
- 单继承链
class B:
age = 3
class A(B):
pass
print(A.age)
A.age = 9
print(B.age)
print(A.age)
print(A.__dict__)
print(B.__dict__)
# 单继承链
class C:
pass
class B(C):
pass
class A(B):
pass
print(A.mro())
# 无重叠的多继承链
class E:
pass
class D:
pass
class C(E):
pass
class B(D):
pass
class A(B, C):
pass
print(A.mro())
# 菱形结构
class D:
pass
class C(D):
pass
class B(D):
pass
class A(B, C):
pass
print(A.mro())
-
针对几种标准原则的方案演化:
inspect
模块inspect.getmro(A)
B.__mro__``B.mro()
查找资源查找顺序
Python2.2之前:
仅仅存在经典类。MRO原则(深度优先–从左往右)。
问题:”有重叠的多继承“中,违背了”重写可用原则“A==>B==>D==>C
。
Python2.2:产生了新式类,MRO原则:
经典类: 深度优先(从左到右)
新式类:
- 在深度优先(从左到右)的算法基础之上,优化了一部分:如果产生重复元素,会保留最后一个,并且更尊重基类出现的先后顺序。注意:并不是广度优先算法。
- 问题:无法检测出有问题的继承;有可能还会违背”局部优先“原则。
Python2.3-2.7:
新式类经典类并存。MRO原则:经典类:深度优先(从左到右),新式类:C3算法。
Python3.x之后:
MRO原则–新式类–C3算法
概念补充:
MRO(Method Resolution Order):方法解析顺序
深度优先(栈):沿着一个继承链,尽可能的往深了去找。
具体步骤:
- 把根节点压入栈中。
- 每次从栈中弹出一个元素,搜索所有在它下一级的元素把这些元素压入栈中(发现已经被处理,则略过)。
- 重复第二个步骤到结束为止。
广度优先(队列):沿着继承链,尽可能往宽了去找。
具体算法步骤:
- 把根节点放到队列的末尾。
- 每次从队列的头部去除一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾(发现已经被处理,则略过)。
- 重复上面步骤。
C3算法:
真正步骤:
两个公式:
L(object)=[object]
L(子类(父类1,父类2)=[子类]+merge(L(父类1),L(父类2),[父类1,父类2]))
,
注意,+代表合并列表,merge算法:
类似拓扑排序,但并不是!如下图错误类关系,拓扑结构无法认证其错误问题。
资源的覆盖:相同属性,子类覆盖父类
根据优先级在优先级较高内的资源写相同的资源,形成一种资源被覆盖的现象。
分类:
- 属性的覆盖。
- 方法重写。
原理: 在MRO的资源检索链当中,优先级比较高的类写了一个和优先级比较低的类一样的一个资源(属性或方法,而摒弃优先级比较低的资源,造成”覆盖“的假象。
注意事项:当调用优先级比较高的资源时,注意self的变化。谁调用,传递谁对象。
# 属性的覆盖
class E:
pass
class D:
age = 1
class C(E):
pass
class B(D):
age = 2
class A(B, C):
pass
print(A.age)
print(A.mro())
# 方法的重写
class E:
pass
class D:
def test(self):
print("D")
class C(E):
pass
class B(D):
def test(self):
print("B")
class A(B, C):
pass
print(A().test())
print(A().test)
print(A.mro())
# A().test 表明 调用A,self为A。
资源的累加
概念:在一个类的基础上,增加一些额外的资源
子类相比父类,多一些自己特有的资源
在被覆盖的方法基础之上,新增内容。
class B:
a1 = 1
class A(B):
a2 = 2
print(A.a1)
print(A.a2)
class B:
a = 1
def __init__(self):
self.b = 2
def test(self):
print('t')
@classmethod
def test1(cls):
print('t1')
@staticmethod
def test2():
print('t2')
class A(B):
c = 3
# def __init__(self): # 在存在自己的初始化函数__init__时,不会调用父类的初始化函数
# self.e = '666' # 解决办法:1: 直接复制父类的内容到子类初始化函数内
# 2.调用:通过类名;通过super
def __init__(self):
B.__init__(self)
self.e = '666'
def ttest(self):
print('t')
@classmethod
def ttest1(cls):
print('t1')
@staticmethod
def ttest2():
print('t2')
pass
p = A()
print(p.a)
print(p.b)
p.test()
p.test1()
p.test2()
print(p.c)
p.ttest()
p.ttest1()
p.ttest2()
p.d = 'xxx'
print(p.d)
print(p.e)
存在问题:
子类在存在自己的初始化函数__init__
时,不会调用父类的初始化函数,造成父类的初始化函数参数无法及时返回到子类中。
解决办法:
- 直接复制父类的内容到子类初始化函数内
- 调用:
- 通过类名(上述例子)
- 通过super函数(建议使用)
# 创建菱形结构
class D(object):
def __init__(self):
print('d')
class B(D):
def __init__(self):
D.__init__(self)
print('d')
class C(D):
def __init__(self):
D.__init__(self)
print('c')
class A(B, C):
def __init__(self):
B.__init__(self)
C.__init__(self)
print('a')
B()
C()
A()
调用类这种方法会在A类中产生重复调用的情况,因此也存在着问题,但在经典类中只能使用这种方法。在新式类中可以采用super
方法。
在低优先级类的方法中,通过"super"调用高优先级类的方法。
概念:是一个类,只有在新式类中有效。
作用:
- 起着代理的作用,帮我们完成以下任务
- 沿着MRO链条,找到下一级节点,去调用对应的方法。注意不是严格的父类关系(菱形关系:
A==>B==>C==>D
,继承关系:A==>B,A==>C,B==>D,C==>D
)
问题:
- 沿着谁的MRO链条?
- 找谁的下一个节点?
- 如何应对类方法,静态方法以及实例方法的传参问题?
语法原理:
* super(参数1[,参数2])
* 工作原理:
def super(cls, inst):
mro = inst.__class__.mro()
retun mro[mro.index(cls)+1]
* 问题解决:
- 沿着谁的MRO链条?答:参数2
- 找谁的下一个节点?答:参数1
- 如何应对类方法,静态方法以及实例方法的传参问题?
答:1.使用参数; 2.进行调用
常用具体语法形式:
# 适用于新式类
# Python2.2+
super(type,obj)->bound super object
# obj当作另外一个实例方法里的参数传递,type是类
super(type, type2)->bound super object
# 调用其他类的类方法,传递给类方法的第一个参数cls
# python 3+
super() #自动输入
通过super调用父类中的方法,注意:第一个参数是类函数,第二个参数是指传递的参数。第一个参数指查找哪一个类的下一个节点,使用第二个参数的MRO链条去调用后续的方法。
class B:
a = 1
def __init__(self):
self.b = 2
def test(self):
print('t')
@classmethod
def test1(cls):
print('t1')
@staticmethod
def test2():
print('t2')
class A(B):
c = 3
def __init__(self):
super(A, self).__init__()
self.e = '666'
def ttest(self):
print('t')
@classmethod
def ttest1(cls):
super(A, cls).test1() # cls或A 都可以,cls也在调用A类。 注意第一个参数为类名称,第二参数为传递参数
print('t1')
@staticmethod
def ttest2():
print('t2')
pass
p = A()
print(p.a)
print(p.b)
p.test()
p.test1()
p.test2()
print(p.c)
p.ttest()
p.ttest1()
p.ttest2()
p.d = 'xxx'
print(p.d)
print(p.e)
print(p.__dict__)
p.ttest1()
菱形问题解决
class D(object):
def __init__(self):
print('d')
class B(D):
def __init__(self):
super(B, self).__init__() # 切记不要用self.__class__代替B,因为self不一定是B中的类,有可能是子类传递过来的self
print('b')
class C(D):
def __init__(self):
super(C, self).__init__()
print('c')
class A(B, C):
def __init__(self):
super().__init__()
print('a')
# B()
# C()
A()
注意事项:
- 切记不要用
super(self.__class__,self)
代替cls
,因为self不一定是cls
中的类,有可能是子类传递过来的self
- 不要通过类名和super混合使用调用资源。
多态
概念:
一个类,所延伸的多种形态;
调用时的多种形态:在继承的前提下;使用不同的子类,调用父类的同一个方法,产生不同的功能
class Animals:
def jiao(self):
pass
class Dog(Animals):
def jiao(self):
print('Wangwang')
class Cat(Animals):
def jiao(self):
print('Miaomiao')
def test(obj):
obj.jiao()
d = Dog()
c = Cat()
test(d)
test(c)
多态在Python中的体现
- 鸭子类型:
- 动态类型的一种风格
- 只要一个对象,会走,会游泳,会叫;那么它就可以当作一个鸭子进行处理
- 关注点在于对象的“行为和属性”,而非对象的“类型”
- 所以,在Python当中,没有真正意义上的多态;也不需要多态
补充
抽象类
一个抽象出来的类,并不是某一个具化的类
不能直接创建实例的类,创建会报错
抽象方法
抽象出来的一个方法(Animal)
不具备具体实现,不能直接调用;子类不是先会报错(jiao)
Python中的实现
- 无法直接支持,需要借助一个模块:
import abc
- 设置类的元类为
abc.ABCMeta
- 使用装饰器修饰抽象方法
@abc.abcstractmethod
import abc
class Animals(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def jiao(self):
pass
@abc.abstractclassmethod
def test(cls):
print('cls')
class Dog(Animals):
def jiao(self):
print('Wangwang')
@classmethod
def test(cls):
print('Dogcls')
pass
class Cat(Animals):
def jiao(self):
print('Miaomiao')
def test(obj):
obj.jiao()
d = Dog()
# c = Cat()
test(d)
# test(c)
d.jiao()
d.test()
注意,在类中使用abc
模块中的方法、静态方法和类方法时,声明之后必须要在运行的子类中也有相应的方法,否则无法运行。
更多推荐
所有评论(0)