请教一下关于 Python 的多继承问题,为什么结果差异很大?

2018-11-13 09:18:18 +08:00
 king1101

相关代码 1:

class A(object):
    def show(self):
        print ('init A...')

class B(A):
    def show(self):
        super(B, self).show()
        print('init B...')
class C(A):
    def show(self):
        # super(C, self).show()
        print('init C...')

class D(B, C):
    def show(self):
        super(D, self).show()
        print('init D...')
d = D()
d.show()

输出的结果是:

init C...
init B...
init D...

这里想问的是为什么没有经过 A,输出 init A...

相关代码 2:

class A(object):
    def show(self):
        print ('init A...')

class B(A):
    def show(self):
        super(B, self).show()
        print('init B...')
class C(A):
    def show(self):
        # super(C, self).show()
        print('init C...')

class D(C, B):    #继承类和代码 1 中的顺序相反
    def show(self):
        super(D, self).show()
        print('init D...')
d = D()
d.show()

输出的结果是:

init C...
init D...

这里想问的是为什么 B 中的方法没有被调用? 还有的就是新式类的 MRO 算法采用广度优先搜索。在这里是怎么调用的?

谢谢各位大佬

2199 次点击
所在节点    Python
12 条回复
Outliver0
2018-11-13 09:40:16 +08:00
你可以打印一下__mro__查看顺序
coroutine
2018-11-13 09:51:09 +08:00
对于, “为什么没有经过 A,输出 init A... ” 你可以参考 Python Cookbook,第 8 章关于 super 的描述。 类 B 里 show 的 super,实际上调用的是类 C 的 show。 而类 C 的 super 已经被你注释掉了。
Outliver0
2018-11-13 09:52:13 +08:00
第一次的 mro 顺序为
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>],
super(D, self).show()----> super(B, self).show()------>C.show()
C 已经有 show()方法,所有打印顺序为 init C...,init B...,init D...


第二次的 mro 顺序为
[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>],
super(D, self).show() ----->C.show()
C 已经有 show()方法,所有打印顺序为 init C... ,init D...
coroutine
2018-11-13 09:58:48 +08:00
super 的调用顺序实际上,与最终该类的 MRO 的顺序有关。你可以从 D.__mro__ 打印出类 D 的类继承顺序。
比如您给出的两个例子,的 MRO 分别为
(__main__.D, __main__.B, __main__.C, __main__.A, object);
(__main__.D, __main__.C, __main__.B, __main__.A, object)
如何生成 MRO 顺序,就是 cookbook 里提到的 https://en.wikipedia.org/wiki/C3_linearization 了。
coroutine
2018-11-13 10:01:31 +08:00
您可以参阅 Python Cookbook Chapter 8: 8.7: Calling a Method on a Parent Class 这一小节。
liukeai7777
2018-11-13 10:11:52 +08:00
D 的 mro 是 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
init C...
init B...
init D...

所以 super 方法的查找顺序就 DBCA 多以第一个例子 第一步运行到 class D 的 show 方法 ,
class D show 第一句:
[super(D, self).show() 查找 B 的 show 方法。运行到 class B 的 show 方法,classB show 执行第一句:super(B, self).show() 然后又找到 class C 的 show 方法 于是打印第一句 init C,执行第二句 init C ]
class D 的 show 第二句 打印 init C

同理第二个例子
MRO 是 DCBA
king1101
2018-11-13 10:12:29 +08:00
多谢各位大佬,我已经理解了,其实走入了一个误区,认为 super 就是调用父类的方法,其实 super 指的是 MRO 的下一个类,和父类没有实质关联。
zxcvsh
2018-11-13 10:13:14 +08:00
百度一下,拓扑排序,好像是这个关键字;图解说明继承检索的顺序很明了
liukeai7777
2018-11-13 10:13:21 +08:00
D 的 mro 是 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
init C...
init B...
init D...

所以 super 方法的查找顺序就 DBCA 多以第一个例子 第一步运行到 class D 的 show 方法 ,
class D show 第一句:
[super(D, self).show() 查找 B 的 show 方法。运行到 class B 的 show 方法,classB show 执行第一句:super(B, self).show() 然后又找到 class C 的 show 方法 于是打印第一句 init C,执行第二句 init B]
class D 的 show 第二句 打印 init D

同理第二个例子
MRO 是 DCBA
seven777
2018-11-13 14:57:56 +08:00
给楼主一个解药:
(我不是程序员,但我觉得如此...)
无论当前的任何编程语言,原理基本想通;
虽然不同语言有不同的“继承”处理方式,但原理相同。
统一标准是:
1,没有什么需求非得复杂的继承来实现,如果有,你设计错了;
2,如果一个继承关系你搞不明白,不是你技术问题,是设计错了,换思路吧;
3,“事不过三”在编程中应该是重要思想,比如继承层级不要超过三层,继承源头不要超过三个,再比如 if,while,等判断关系嵌套不要超过三层...
我瞎说的,但觉得应该如此
ticotico
2018-11-13 15:57:49 +08:00
不是把 C 中 super 注释掉了么,D 中的 super 是按 mro 查找 MRO 中下一位的,MRO 在新式类中按的是 bfs 来拍得,最近刚接触 mro 不知道说得对不对,各位大佬轻拍
RomanCavalry
2018-11-15 15:00:16 +08:00
这个就需要了解 Python MRO 的历史了,参考: http://python.jobbole.com/85685/

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/507242

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX