构造D(B,C)菱形继承,验证B.greet()里的super().greet()调用到的是C而不是A,看super()到底是按什么规则决定调用谁的。
B(A)、C(A)、D(B,C),每个类的greet都调用super().greet()。
代码class A: def greet(self): print("A.greet 被调用") class B(A): def greet(self): print("B.greet 被调用,调用super()...") super().greet() class C(A): def greet(self): print("C.greet 被调用,调用super()...") super().greet() class D(B, C): def greet(self): print("D.greet 被调用,调用super()...") super().greet()
这是本文最核心的发现。
结果D.__mro__: ['D', 'B', 'C', 'A', 'object'] 调用d.greet(),完整输出: D.greet 被调用,调用super()... B.greet 被调用,调用super()... C.greet 被调用,调用super()... ← 是C,不是A! A.greet 被调用
的调用链是 D→B→C→A,不是"D调用B,B调用它自己的父类A"。B 在类定义上唯一声明的父类确实是 A(class B(A)),但 super().greet() 不是"调用B声明的父类的greet",而是"沿着当前实例d的完整MRO,找到B的下一个位置,调用那个类的greet"——因为这次调用的实例是 d(一个D的实例),它的MRO是D→B→C→A,B的下一个是C,所以 super() 在这里调用到了C,跟B自己在定义时声明的父类完全没关系。
super() 容易被误解的地方:很多人以为 super().method() 等价于"调用我父类的这个方法",但它调用的是"当前实例的MRO里,我后面那一个"——如果这个实例是通过多继承创建的子类实例,"我后面那一个"可能是一个跟我毫无继承关系(同辈)的兄弟类,不是我自己声明的父类。super() 的行为取决于运行时这个具体实例的MRO,不是类定义时写的继承关系本身。
如果super()只认"类定义时声明的父类",会发生什么。
如果 B.greet() 里的 super() 死板地指向 A,那么在 D(B,C) 这个组合里,C.greet() 就永远没有机会被调用到——调用链会是 D→B→A,C被跳过。这在需要"每一层都做一点初始化/清理工作,然后交给下一层继续"的场景(比如多个mixin类协作)下是致命的。super() 按MRO走,才能保证不管这个类被用在多复杂的继承组合里,只要每一层都老老实实调用 super(),整条链都能完整地走下去,谁都不会被落下。
每一步都是本文上面用Python 3.14跑出来的原始输出。
super() / __mro__:验证super()按MRO而非声明父类调用