笔记4:由super()学习python的继承机制以及C3算法
python super():
__init__()
是 Python 的构造方法。
class Bird:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print 'Ahahahah'
else:
print 'No thanks!'
class SongBird(Bird):
def __init__(self):
self.sound = 'Squawk'
def sing(self):
print self.sound
sb = SongBird()
sb.sing() # 能正常输出
sb.eat() # 报错,因为 songgird 中没有 hungry 特性
在这个代码上,由于父类没有构造,导致sb.eat() 不能调用。
解决这个就要用以下两种方法:
def __init__(self):
Bird.__init__(self)
self.sound = 'Squawk'
#原理:在调用了一个实例的方法时,该方法的self参数会自动绑定到实例上(称为绑定方法);如果直接调用类的方法(比如Bird.__init__),那么就没有实例会被绑定,可以自由提供需要的self参数(未绑定方法)。
#或者
def __init__(self):
super(SongBird,self).__init__()
self.sound = 'Squawk'
#它会查找所有的超类,以及超类的超类,直到找到所需的特性为止。
现在B类继承A类,我们要给B类一个独特的初始属性,不能写__init__(self)
,因为这样写就相当于重写父类的。super(B,self).__init__( )
class A():
def __init__(self):
self.first_name = 'king'
def second_name(self):
print('hao')
class B(A):
def __init__(self):
super(B,self).__init__( )#同时也可以写成B.__init__(self),此处可以传参数
self.english_name = 'horsun'
def full_name(self):
print('king hao')
注意:class后面的继承顺序init里面优先,其次才是class里面的顺序有关,
注意:当一个类继承2个单独的类,而这两个类又继承自同一个类,,此时,会多次调用顶端类的"__init__()
"。
class MyBaseClass:
def __init__(self, value):
self.value = value
class TimesFive(MyBaseClass):
def __init__(self, value):
MyBaseClass.__init__(self, value)
self.value *= 5
class PlusTwo(MyBaseClass):
def __init__(self, value):
MyBaseClass.__init__(self, value)
self.value += 2
class ThisWay(TimesFive, PlusTwo):
def __init__(self, value):
TimesFive.__init__(self, value)
PlusTwo.__init__(self, value)
foo = ThisWay(5)
print('Should be (5*5)+2 but is ', foo.value)#7
解决上面的问题用如下
class MyBaseClass:
def __init__(self, value):
self.value = value
class TimesFive(MyBaseClass):
def __init__(self, value):
super(TimesFive, self).__init__(value)
self.value *= 5
class PlusTwo(MyBaseClass):
def __init__(self, value):
super(PlusTwo, self).__init__(value)
self.value += 2
class GoodWay(TimesFive, PlusTwo):
def __init__(self, value):
super(GoodWay, self).__init__(value)
foo = GoodWay(5)
print('Should be 5*(5+2) and is ', foo.value)#35
print(GoodWay.mro())#[<class '__main__.GoodWay'>, <class '__main__.TimesFive'>, <class '__main__.PlusTwo'>, <class '__main__.MyBaseClass'>, <class 'object'>]
python继承算法,最常用MRO C3算法:
最开始Python使用经典类的MRO(深度优先)b->[a,c]->d,搜索顺序为B,A,D,C导致C中的最后找,不合理。
后来新式类的MRO(广度优先)(判断好节点,即N之后的节点都不继承自N,N就是好节点)B,A,D,C,D,但是发现D不是好节点,去掉变为B,A,C,D
C3算法解决了单调性问题和只能继承无法重写问题。
单调性:不符合顺序,即新式中,明明B,A,D,但是却不按这个顺序
继承无法重写:本质是新式中先访问了子类的父类:使用拓扑排序
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
且该序列必须满足下面两个条件:
每个顶点出现且只出现一次。
若存在一条从顶点A到顶点B的路径,那么在序列中顶点A出现在顶点B的前面。

{1,2,4,3,5}
C3我感觉是拓扑算法的反向。
C3算法实现了三种重要特性:
-
保持继承
-
保证局部优先原则(比如A继承C,C继承B,那么A读取父类方法,应该优先使用C的方法而不是B的方法)。
-
保证单调性原则(即子类不改变父类的方法搜索顺序)。
具体如下
1.
class B(A)
这时B的mro序列为[B,A]
2.
class B(A1,A2,...,An)
这时B的mro序列:
mro(B) = [B] + merge(mro(A1), mro(A2),...,mro(An), [A1,A2,...,An])
merge操作就是C3算法的核心,是递归运算。
遍历执行merge操作的序列,如果一个序列的第一个元素,在其他序列如果出现也是第一个元素,则从所有执行merge操作序列中删除这个元素,合并到当前的mro中。merge操作后的序列,递归地执行merge操作,直到merge操作的序列为空。
如果merge操作的序列无法为空,则说明不合法。
看人话:
class A(object):pass class B(object):pass class C(object):pass class E(A,B):pass class F(B,C):pass class G(E,F):pass
A、B、C都继承至一个基类,所以mro序列依次为[A,O]、[B,O]、[C,O]
执行merge操作的序列为[A,O]、[B,O]、[A,B] A是序列[A,O]中的第一个元素,在序列[B,O]中不出现,在序列[A,B]中也是第一个元素,所以从执行merge操作的序列([A,O]、[B,O]、[A,B])中删除A,合并到当前mro,[E]中。
接着B也满足,
cls.mro()
就会打印MRO List。
举个例子
class A: def method(self): print("CommonA") class B(A): pass class C(A): def method(self): print("CommonC") class D(B, C): pass print(D().method())
返回
CommonC
由此我们可以进一步学习python多重继承到底是继承谁
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器