小甲鱼 发表于 2014-6-24 20:50:14

多重继承的陷阱:钻石继承(菱形继承)问题

多重继承的陷阱:钻石继承(菱形继承)问题


支持多继承的面向对象编程都可能会导致钻石继承(菱形继承)问题,看以下代码:

class A():
    def __init__(self):
      print("进入A…")
      print("离开A…")

class B(A):
    def __init__(self):
      print("进入B…")
      A.__init__(self)
      print("离开B…")
      
class C(A):
    def __init__(self):
      print("进入C…")
      A.__init__(self)
      print("离开C…")

class D(B, C):
    def __init__(self):
      print("进入D…")
      B.__init__(self)
      C.__init__(self)
      print("离开D…")

>>> d = D()
进入D…
进入B…
进入A…
离开A…
离开B…
进入C…
进入A…
离开A…
离开C…
离开D…


为什么叫钻石继承(菱形继承),看下图就明白名字的由来了:




钻石继承(菱形继承)会带来什么问题?

多重继承容易导致钻石继承(菱形继承)问题,上边代码实例化 D 类后我们发现 A 被前后进入了两次(有童鞋说两次就两次憋,我女朋友还不止呢……)。

这有什么危害?我举个例子,假设 A 的初始化方法里有一个计数器,那这样 D 一实例化,A 的计数器就跑两次(如果遭遇多个钻石结构重叠还要更多),很明显是不符合程序设计的初衷的(程序应该可控,而不能受到继承关系影响)。


如何避免钻石继承(菱形继承)问题?

为解决这个问题,Python 使用了一个叫“方法解析顺序(Method Resolution Order,MRO)”的东西,还用了一个叫 C3 的算法。

该算法相对来说比较复杂(有兴趣深入算法的朋友可以阅读:https://www.python.org/download/releases/2.3/mro)

当然我这里愿意跟你解释下 MRO 的顺序基本就是:在避免同一类被调用多次的前提下,使用广度优先和从左到右的原则去寻找需要的属性和方法。

在继承体系中,C3 算法确保同一个类只会被搜寻一次。例子中,如果一个属性或方法在 D 类中没有被找到,Python 就会搜寻 B 类,然后搜索 C类,如果都没有找到,会继续搜索 B 的基类 A,如果还是没有找到,则抛出“AttributeError”异常。

你可以使用 类名.__mro__ 获得 MRO 的顺序(注:object 是所有类的基类,金字塔的顶端):>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
其实你大可不必为上边的内容而烦恼,因为这时候你应该召唤 super 函数大显神威:
class A():
    def __init__(self):
      print("进入A…")
      print("离开A…")

class B(A):
    def __init__(self):
      print("进入B…")
      super().__init__()
      print("离开B…")
      
class C(A):
    def __init__(self):
      print("进入C…")
      super().__init__()
      print("离开C…")

class D(B, C):
    def __init__(self):
      print("进入D…")
      super().__init__()
      print("离开D…")

>>> d = D()
进入D…
进入B…
进入C…
进入A…
离开A…
离开C…
离开B…
离开D…


许小恩 发表于 2014-6-25 10:49:22

受教了!!!

hfut_wangfei 发表于 2014-7-4 17:01:55


受教了!!!

kingdom412 发表于 2014-7-24 12:16:23

长知识了....................

cryingclown 发表于 2014-7-31 23:50:07

MM2M-AGJG-JUGP-Y9AH

cryingclown 发表于 2014-7-31 23:51:00

cryingclown 发表于 2014-7-31 23:50
MM2M-AGJG-JUGP-Y9AH

额 发错了

kiralgk 发表于 2014-12-10 09:49:28

{:5_90:}

瞬秒爆加速 发表于 2014-12-21 16:35:04

不明觉厉

菜子zzk 发表于 2015-2-8 18:54:23

学习了!

python001 发表于 2015-2-10 11:12:31

学习

小金丝熊 发表于 2015-3-6 10:25:37

good!

小强。。。 发表于 2015-3-7 22:18:51

:shock:

dothink 发表于 2015-3-21 10:26:02

原来如此

LearnGG 发表于 2015-4-15 21:30:59

super()对于多继承蛮有优势啊~

赵为 发表于 2015-5-7 14:25:54

牛逼的super()

_gcc 发表于 2015-7-27 21:33:24

C++中的虚基类

xiongjingkui 发表于 2015-8-3 14:48:52

以前一直不知道super()方法和直接用类名的方法有什么区别,原来区别在这啊,受教了{:9_228:}

roar123 发表于 2015-9-16 21:36:57

回去再看看super(){:5_90:}

123456774653 发表于 2015-9-19 20:53:55

受教了

loco_v 发表于 2015-10-20 11:24:36

用super就避免了多重继承的重复计数的问题
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 多重继承的陷阱:钻石继承(菱形继承)问题