,

python 类继承中MRO

1. 先查看 C 的 MRO 继承链 super() 的逻辑:不是固定找直接父类,而是沿着 MRO 列表找下一…

class Base(object):
    def __init__(self):
        print("enter Base")
        print("leave Base")

class A(Base):
    def __init__(self):
        print("enter A")
        super().__init__()
        print("leave A")

class B(Base):
    def __init__(self):
        print("enter B")
        super().__init__()
        print("leave B")

class C(A, B):
    def __init__(self):
        print("enter C")
        super().__init__()
        print("leave C")

c = C()
enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C

1. 先查看 C 的 MRO 继承链

print(C.__mro__)
# 输出:(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)

super() 的逻辑:不是固定找直接父类,而是沿着 MRO 列表找下一个类

2. 逐行拆解执行流程

  1. c = C() → 执行 C.__init__
    • 打印 enter C
    • super().__init__():MRO 中 C 的下一个是 A → 进入 A.__init__
  2. 执行 A.__init__
    • 打印 enter A
    • super().__init__():MRO 中 A 的下一个是 B → 进入 B.__init__
  3. 执行 B.__init__
    • 打印 enter B
    • super().__init__():MRO 中 B 的下一个是 Base → 进入 Base.__init__
  4. 执行 Base.__init__
    • 打印 enter Base
    • 打印 leave Base Base 没有 super,本层构造函数结束,逐层回退
  5. 回到 B 剩余代码:打印 leave B
  6. 回到 A 剩余代码:打印 leave A
  7. 回到 C 剩余代码:打印 leave C

三、关键易错点区分

  1. 如果不用 super(),手动写 Base.__init__(self): A 和 B 都会单独调用 Base,Base 会被执行两次;
  2. super() 依靠 MRO 保证公共父类 Base 只初始化一次(钻石继承问题解决方案);
  3. 打印规律:先从上到下「enter」进入各个子类,再从最底层父类往回逐层打印「leave」。

疑问 1:为什么 A 没有直接进入 Base?

因为多继承 MRO 重排了继承顺序:

C(A,B) 的 MRO 是 C,A,B,Base

在 A 中写 super(),解释器去 MRO 找 A 的下一位,是 B,不是 Base。

A 根本不会直接调用 Base,而是交给 B 去执行。

疑问 2:为什么 B 执行完 Base,A 就跟着结束了?

A 内部逻辑只有三行:

print("enter A")
super().__init__()  # 整段B+Base的逻辑全部在这里跑完
print("leave A")

super().__init__() 是阻塞调用,必须等它里面所有嵌套代码(B、Base 全部执行完、全部 return),才会执行下一行 print("leave A")

流程等价嵌套函数调用:

C{
  enter C
  A{
    enter A
    B{
      enter B
      Base{
        enter Base
        leave Base
      }
      leave B
    }
    leave A
  }
  leave C
}

对比:如果不用 super,手动写父类会怎么样?

假如 A 里不写 super,直接硬编码 Base.__init__(self)

class A(Base):
    def __init__(self):
        print("enter A")
        Base.__init__(self)
        print("leave A")

此时 A 会直接调用 Base,B 也会再调用一次 Base,Base 执行两遍,这就是钻石继承的重复初始化坑,super()+MRO 就是为了解决这个问题。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

About the Author

每个人都有自己得时区,在自己得时区里,一切都是准时的。

BlockSpare — News, Magazine and Blog Addons for (Gutenberg) Block Editor