鱼C论坛

 找回密码
 立即注册
查看: 90|回复: 1

钻石继承课程中例题里面关于MRO的困惑

[复制链接]
发表于 2025-4-6 14:52:29 | 显示全部楼层 |阅读模式

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

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

x
小甲鱼老师在讲解钻石继承时举以下两个例子.

[例题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顺序都一样([__main__.C, __main__.B1, __main__.B2, __main__.A, object]).





小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-4-6 14:53:30 | 显示全部楼层
关于钻石继承中super()和MRO顺序的解答

你的困惑源于对super()工作机制和MRO(方法解析顺序)执行路径的理解差异。让我们分步分析:

  • 关键点1:MRO顺序是固定的
    通过C.mro()可以看到继承顺序都是:

    1. [<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(显式调用):

    1. C → 手动调用B1 → A → 手动调用B2 → A
    复制代码

    结果:A被初始化两次,顺序由代码写死
  • 案例2(super()调用):

    1. C → B1 → B2 → A → object
    复制代码

    执行路径是单链式的:C的super()→B1的super()→B2的super()→A的super()


为什么B2的输出在B1之前?

因为super()的调用是反直觉的"先纵深后回溯":

  1. C.__init__()
  2. └→ B1.__init__()        ← 先进入B1
  3.    └→ B2.__init__()     ← B1的super()指向B2
  4.       └→ A.__init__()   ← B2的super()指向A
复制代码


当A完成初始化后,才会依次返回执行:
A → 打印 → 返回到B2 → 打印 → 返回到B1 → 打印 → 返回到C → 打印

这就是为什么最终输出顺序是:
A → B2 → B1 → C

总结
super()通过MRO建立了一个严谨的调用链,确保每个父类方法只被调用一次。显式调用父类方法会破坏这个链式机制,导致重复初始化和顺序不可控。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-5-5 20:22

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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