1、类为经典类时,多继承情况下,会按照深度优先查找
2、类为新式类时,多继承情况下,会按照广度优先查找
class A:
def test(self):
print('excute A.test()')
class B(A):
pass
class C(A):
def test(self):
print('excute C.test()')
class D(B, C):
pass
if __name__ == '__main__':
d = D()
d.test()
B、C 是 A 的子类,D 多继承了 B、C 两个类,其中 C 重写了 A 中的 test() 方法。
如果 A 是经典类(如上代码),当调用 D 的实例的 test() 方法时,Python 会按照深度优先的方法去搜索 test() ,路径是 B-A-C ,执行的是 A 中的 test() ;如果 A 是新式类,当调用 D 的实例的 test() 方法时,Python 会按照广度优先的方法去搜索 test() ,路径是 B-C-A ,执行的是 C 中的 test() 。
因为 D 是直接继承 C 的,从逻辑上说,执行 C 中的 test() 更加合理,因此新式类对多继承的处理更为合乎逻辑。
在 Python 3.x 中的新式类貌似已经兼容了经典类,无论 A 是否继承 object 类, D 实例中的 test() 都会执行 C 中的 test() 。
但是在 Python 2.7 中这种差异仍然存在,因此还是推荐使用新式类,要继承 object 类。
MRO:Method Resolution Order,即方法解析顺序,是python中用于处理二义性问题的算法
python支持多继承,多继承的语言往往会遇到以下两类二义性的问题:
C++也是支持多继承的语言之一
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑排序(TopologicalOrder)的序列,简称拓扑序列。
拓扑排序的实现步骤:
循环执行以下两步,直到不存在入度为0的顶点为止
class A(object):
def __init__(self):
self.name = "A: name"
print "A:__init__"
def fun(self):
print "A:fun"
class B(A):
def __init__(self):
print "B:__init__"
A.__init__(self) # 使用类名直接调用
super(B, self).__init__() # 使用super关键字调用
def fun(self):
print "B:fun"
A.fun(self)
super(B, self).fun()
print self.name
class A(object):
def __init__(self):
print "enter A"
print "leave A"
class B(object):
def __init__(self):
print "enter B"
print "leave B"
class C(A):
def __init__(self):
print "enter C"
super(C, self).__init__()
print "leave C"
class D(A):
def __init__(self):
print "enter D"
super(D, self).__init__()
print "leave D"
class E(B, C):
def __init__(self):
print "enter E"
B.__init__(self)
C.__init__(self)
print "leave E"
class F(E, D):
def __init__(self):
print "enter F"
E.__init__(self)
D.__init__(self)
print "leave F"
f = F()
print(F.__mro__)
enter F
enter E
enter B
leave B
enter C
enter D
enter A
leave A
leave D
leave C
leave E
enter D
enter A
leave A
leave D
leave F
(<class ‘main.F’>, <class’main.E’>, <class’main.B’>, <class’main.C’>, <class’main.D’>, <class’main.A’>,
<type ‘object’>)
在python 2.2之前,python中使用经典类(classicclass),经典类是一种没有继承的类,所有类型都是type类型,如果经典类作为父类,子类调用父类构造函数会报错。当时用作MRO的算法是DFS(深度优先),下面的例子是当时使用DFS算法的示例(向右是基类方向):
在python2.2开始,为了使类的内置类型更加统一,引入了新式类(new-style class),新式类每个类都继承自一个基类,默认继承自object,子类可以调用基类的构造函数。由于所有类都有一个公共的祖先类object,所以新式类不能使用DFS作为MRO。在当时有两种MRO并存:
如果是经典类,MRO使用DFS
如果是新式类,MRO使用BFS
针对新式类的BFS示例如下(向右是基类方向):
正常继承方式:
A->B->D
A->C->E
BFS的遍历顺序为:A->B->C->D->E
D是B的唯一基类,但是遍历时却先遍历节点C,这种情况下应该先从唯一基类进行查找,这个原则称为单调性。
菱形的继承方式
A->B->D
A->C->D
BFS的遍历顺序为:A->B->C->D
BFS解决了前面提到的子类无法重写基类方法的问题。
由于DFS和BFS针对经典类和新式类都有缺陷,从python2.3开始,引入了C3算法。针对前面两个例子,C3算法的遍历顺序如下:
看起来是DFS和BFS的综合,但是并非如此,下面的例子说明了C3算法的具体实现:
从前面拓扑排序的定义可知,将有向无环图进行拓扑排序后,按照得到的拓扑序列遍历即可满足单调性,原因是由根到叶即是子类到基类的方向,当基类的入度为0是,它就是子类的唯一基类,此时会优先遍历此基类,符合单调性。而子类无法重写方法的问题也可以得到解决,因为当多个子类继承自同一个基类时,该基类的入度不会先于子类减为0,所以可以保证优先遍历入度减为0的子类。
结合下面这张图的例子来说明C3算法的执行步骤(图中箭头由子类指向父类):
class D(object):
pass
class E(object):
pass
class F(object):
pass
class C(D, F):
pass
class B(E, D):
pass
class A(B, C):
pass
print A.__mro__
#输出:(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>, <type 'object'>)
参考链接:https:///qwertyupoiuytr/article/details/539134
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- oldu.cn 版权所有 浙ICP备2024123271号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务