LEEFEI571 发表于 2025-4-6 14:52:29

钻石继承课程中例题里面关于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顺序都一样().





FishC 发表于 2025-4-6 14:53:30

关于钻石继承中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]
查看完整版本: 钻石继承课程中例题里面关于MRO的困惑