Python正课74 —— 多继承带来的菱形问题
本文内容皆为作者原创,如需转载,请注明出处:https://www.cnblogs.com/xuexianqi/p/12669925.html
一:菱形问题
大多数面向对象语言都不支持多继承,而在Python中,一个子类是可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的 Diamond problem菱形问题(或称钻石问题,有时候也被称为“死亡钻石”),菱形其实就是对下面这种继承结构的形象比喻。
class A(object):
def test(self):
print('from A')
pass
class B(A):
def test(self):
print('from B')
pass
class C(A):
def test(self):
print('from C')
pass
class D(C, B):
# def test(self):
# print('from D')
pass
# print(D.mro()) # 类D以及类D的对象访问属性都是参照该类的mro列表
# 输出:[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
obj = D()
obj.test() # from C
print(D.test) # <function C.test at 0x039D84A8>
print(C.mro()) # 类C以及类C的对象访问属性都是参照该类的mro列表
# 输出:[<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
c = C()
c.test() # from C
总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro
python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
二:如果多继承是非菱形继承,经典类与新式的属性查找顺序一样:
都是一个分支一个分支地找下去,然后最后找object
class E:
# def test(self):
# print('from E')
pass
class F:
def test(self):
print('from F')
class B(E):
# def test(self):
# print('from B')
pass
class C(F):
# def test(self):
# print('from C')
pass
class D:
def test(self):
print('from D')
class A(B, C, D):
# def test(self):
# print('from A')
pass
# 新式类
# print(A.mro()) # A->B->E->C->F->D->object
obj = A()
obj.test() # 结果为:from F
三:如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:
经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索大脑袋(共同的父类)
新式类:广度优先,会在检索最后一条分支的时候检索大脑袋
class G: # 在python2中,未继承object的类及其子类,都是经典类
# def test(self):
# print('from G')
pass
class E(G):
# def test(self):
# print('from E')
pass
class F(G):
def test(self):
print('from F')
class B(E):
# def test(self):
# print('from B')
pass
class C(F):
def test(self):
print('from C')
class D(G):
def test(self):
print('from D')
class A(B, C, D):
# def test(self):
# print('from A')
pass
# 新式类
# print(A.mro()) # A->B->E->C->F->D->G->object
# 经典类:A->B->E->G->C->F->D
obj = A()
obj.test() #