鱼C论坛

 找回密码
 立即注册
查看: 58822|回复: 203

[扩展阅读] 多重继承的陷阱:钻石继承(菱形继承)问题

    [复制链接]
发表于 2014-6-24 20:50:14 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

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


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

  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.         
  11. class C(A):
  12.     def __init__(self):
  13.         print("进入C…")
  14.         A.__init__(self)
  15.         print("离开C…")

  16. class D(B, C):
  17.     def __init__(self):
  18.         print("进入D…")
  19.         B.__init__(self)
  20.         C.__init__(self)
  21.         print("离开D…")

  22. >>> d = D()
  23. 进入D…
  24. 进入B…
  25. 进入A…
  26. 离开A…
  27. 离开B…
  28. 进入C…
  29. 进入A…
  30. 离开A…
  31. 离开C…
  32. 离开D…
复制代码


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

BaiduShurufa_2014-6-24_18-34-9.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.         
  11. class C(A):
  12.     def __init__(self):
  13.         print("进入C…")
  14.         super().__init__()
  15.         print("离开C…")

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

  21. >>> d = D()
  22. 进入D…
  23. 进入B…
  24. 进入C…
  25. 进入A…
  26. 离开A…
  27. 离开C…
  28. 离开B…
  29. 离开D…
复制代码


评分

参与人数 30荣誉 +103 鱼币 +89 贡献 +57 收起 理由
hualinxuept + 2 + 2 + 2 鱼C有你更精彩^_^
知-更-鸟 + 1 + 3
Solarbear + 5
MR.GREEN + 5 + 5 + 3
0惨绿少年0 + 3 + 2 + 1
学到扛不住 + 5 + 5 + 3 鱼C有你更精彩^_^
sunnyLHS + 5 + 5 + 3 鱼C有你更精彩^_^
天马流星拳 + 5 + 5 + 3 鱼C有你更精彩^_^
一个账号 + 2 + 2 + 1 感谢楼主无私奉献!
无声好 + 3 鱼C有你更精彩^_^

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2014-6-25 10:49:22 | 显示全部楼层
受教了!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-7-4 17:01:55 | 显示全部楼层

受教了!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2014-7-24 12:16:23 | 显示全部楼层
长知识了....................
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-7-31 23:50:07 | 显示全部楼层
MM2M-AGJG-JUGP-Y9AH
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-7-31 23:51:00 | 显示全部楼层

额 发错了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-12-10 09:49:28 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2014-12-21 16:35:04 | 显示全部楼层
不明觉厉
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2015-2-8 18:54:23 | 显示全部楼层
学习了!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-2-10 11:12:31 | 显示全部楼层
学习
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2015-3-6 10:25:37 | 显示全部楼层
good!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2015-3-7 22:18:51 | 显示全部楼层
:shock:
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2015-3-21 10:26:02 | 显示全部楼层
原来如此
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-4-15 21:30:59 | 显示全部楼层
super()对于多继承蛮有优势啊~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-5-7 14:25:54 | 显示全部楼层
牛逼的super()
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2015-7-27 21:33:24 | 显示全部楼层
C++中的虚基类
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-3 14:48:52 | 显示全部楼层
以前一直不知道super()方法和直接用类名的方法有什么区别,原来区别在这啊,受教了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-9-16 21:36:57 | 显示全部楼层
回去再看看super()
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-9-19 20:53:55 | 显示全部楼层
受教了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-10-20 11:24:36 | 显示全部楼层
用super就避免了多重继承的重复计数的问题
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-3-29 08:20

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表