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

迈不过友情╰ 2022-04-21 14:38 429阅读 0赞

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

  1. class A():
  2. def __init__(self):
  3. print("进入A…")
  4. print("离开A…")
  5. class B(A):
  6. def __init__(self):
  7. print("进入B…")
  8. A.__init__(self)
  9. print("离开B…")
  10. class C(A):
  11. def __init__(self):
  12. print("进入C…")
  13. A.__init__(self)
  14. print("离开C…")
  15. class D(B, C):
  16. def __init__(self):
  17. print("进入D…")
  18. B.__init__(self)
  19. C.__init__(self)
  20. print("离开D…")
  21. >>> d = D()
  22. 进入D
  23. 进入B
  24. 进入A
  25. 离开A
  26. 离开B
  27. 进入C
  28. 进入A
  29. 离开A
  30. 离开C
  31. 离开D

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

205047m7l6h7bj0xr77x7r.png

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

多重继承容易导致钻石继承(菱形继承)问题,上边代码实例化 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 是所有类的基类,金字塔的顶端):

  1. >>> D.__mro__
  2. (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

其实你大可不必为上边的内容而烦恼,因为这时候你应该召唤 super 函数大显神威:

  1. class A():
  2. def __init__(self):
  3. print("进入A…")
  4. print("离开A…")
  5. class B(A):
  6. def __init__(self):
  7. print("进入B…")
  8. super().__init__()
  9. print("离开B…")
  10. class C(A):
  11. def __init__(self):
  12. print("进入C…")
  13. super().__init__()
  14. print("离开C…")
  15. class D(B, C):
  16. def __init__(self):
  17. print("进入D…")
  18. super().__init__()
  19. print("离开D…")
  20. >>> d = D()
  21. 进入D
  22. 进入B
  23. 进入C
  24. 进入A
  25. 离开A
  26. 离开C
  27. 离开B
  28. 离开D

发表评论

表情:
评论列表 (有 0 条评论,429人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C++:53---菱形继承、虚继承

    一、菱形继承 在介绍虚继承之前介绍一下菱形继承 概念:A作为基类,B和C都继承与A。最后一个类D又继承于B和C,这样形式的继承称为菱形继承 菱形继承的缺

    相关 菱形继承

    两个子类继承同一个父类,而又有子类又分别继承这两个子类,就称作菱形继承 ![70][] 多重继承产生的二义性 假设有一个基类,他派生了两个子类分别继承于它,比如说下面

    相关 菱形继承问题

      在java中只允许单继承,至于原因就需要牵扯到“菱形继承问题”。 菱形继承问题   在介绍之前首先看一张图:   ![在这里插入图片描述][watermark_