钻石继承课程中例题里面关于MRO的困惑
小甲鱼老师在讲解钻石继承时举以下两个例子.[例题2]
第一个例子没有使用super()构造函数,会导致钻石继承问题,例题如下:
>>> class A:
... def __init__(self):
... print("哈喽,我是A~")
...
>>> class B1(A):
... def __init__(self):
... A.__init__(self)
... print("哈喽,我是B1~")
...
>>> class B2(A):
... def __init__(self):
... A.__init__(self)
... print("哈喽,我是B2~")
...
>>> class C(B1, B2):
... def __init__(self):
... B1.__init__(self)
... B2.__init__(self)
... print("哈喽,我是C~")
>>> c = C()
执行结果如下:
哈喽,我是A~
哈喽,我是B1~
哈喽,我是A~
哈喽,我是B2~
哈喽,我是C~
这里” 哈喽,我是B1~”是在” 哈喽,我是B2~”的上面.
[例题2]
第二个例子使用super()构造函数,解决钻石继承问题,例题如下:
>>> class B1(A):
... def __init__(self):
... super().__init__()
... print("哈喽,我是B1~")
...
>>> class B2(A):
... def __init__(self):
... super().__init__()
... print("哈喽,我是B2~")
...
>>> class C(B1, B2):
... def __init__(self):
... super().__init__()
... print("哈喽,我是C~")
...
>>> c = C()
执行结果如下:
哈喽,我是A~
哈喽,我是B2~
哈喽,我是B1~
哈喽,我是C~
[问题]
我的困惑是在使用super()语句解决钻石问题后,为何执行结果却是将” 哈喽,我是B2~”变成在” 哈喽,我是B1~”的上面?虽然这里类C的MRO顺序都一样().
”
关于钻石继承中super()和MRO顺序的解答
你的困惑源于对super()工作机制和MRO(方法解析顺序)执行路径的理解差异。让我们分步分析:
[*]关键点1:MRO顺序是固定的
通过C.mro()可以看到继承顺序都是:
[<class 'C'>, <class 'B1'>, <class 'B2'>, <class 'A'>, <class 'object'>]
[*]关键点2:显式调用与super()的区别
B1.__init__()和B2.__init__()是直接调用父类方法,而super()会根据MRO动态寻找下一个类
[*]关键点3:super()的链式调用过程
当调用C的构造函数时:
1. C的super()找到B1的__init__
2. B1的super()根据MRO找到下一个类B2的__init__
3. B2的super()找到A的__init__
执行流程对比
[*]案例1(显式调用):
C → 手动调用B1 → A → 手动调用B2 → A
结果:A被初始化两次,顺序由代码写死
[*]案例2(super()调用):
C → B1 → B2 → A → object
执行路径是单链式的:C的super()→B1的super()→B2的super()→A的super()
为什么B2的输出在B1之前?
因为super()的调用是反直觉的"先纵深后回溯":
C.__init__()
└→ B1.__init__() ← 先进入B1
└→ B2.__init__() ← B1的super()指向B2
└→ A.__init__() ← B2的super()指向A
当A完成初始化后,才会依次返回执行:
A → 打印 → 返回到B2 → 打印 → 返回到B1 → 打印 → 返回到C → 打印
这就是为什么最终输出顺序是:
A → B2 → B1 → C
总结
super()通过MRO建立了一个严谨的调用链,确保每个父类方法只被调用一次。显式调用父类方法会破坏这个链式机制,导致重复初始化和顺序不可控。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页:
[1]