Python调用D().method()不按继承声明顺序执行,是因为其采用C3线性化算法生成静态、唯一的MRO顺序(如D→B→C→A→object),而非简单从左到右或深度优先;super()依此顺序查找下一个类,菱形继承中A必唯一且居后,MRO在类定义时冻结不可变。

为什么调用 D().method() 时不总是按继承声明顺序执行
因为 Python 不是简单地“从左到右”或“深度优先”找方法,而是按 __mro__ 元组里计算出的唯一扁平化顺序查找。比如 class D(B, C): pass,即使 B 和 C 都定义了 method,最终调用哪个不取决于谁写在前面,而取决于整个继承图经 C3 算法合并后的结果——B 在 C 前面,仅当它们的 MRO 能被无冲突合并时才成立。
常见错误现象:
- 预期
D().method()调用C.method,实际执行了B.method - 修改某个父类(如让
B继承A,C也继承A)后,D.__mro__突然报错:TypeError: Cannot create a consistent method resolution order (MRO)
super() 的行为完全依赖 __mro__,不是“调第一个父类”
调用 super().foo() 时,Python 会从当前类在 __mro__ 中的**下一个位置**开始向后搜索,而不是固定去第一个父类。这意味着:
- 如果当前类是
D,D.__mro__是(D, B, C, A, object),那么super().foo()从B开始查 - 若
B.foo内部也有super().foo(),它会继续从C开始查,而非回到A或跳过C - 只要任一环节没用
super(),链条就中断——比如B.foo直接写A.foo(self),C.foo就永远不会被触发
菱形继承中 A 必须且只能出现在 __mro__ 末尾一次
当 B 和 C 都继承 A,而 D 同时继承 B 和 C,C3 算法强制保证 A 出现在所有父类之后、且只出现一次。否则会出现语义矛盾:比如 A.__init__ 被执行两次,或某些初始化逻辑被跳过。
立即学习“Python免费学习笔记(深入)”;
这直接影响实际设计:
- 不能靠手动控制调用顺序来“绕过”MRO,比如在
D.__init__里分别调B.__init__(self)和C.__init__(self)—— 这会导致A.__init__执行两次 - 所有协作式初始化必须统一用
super().__init__(),且所有父类都得配合,否则 MRO 链断裂 -
A类本身不能在自己的__init__里再调super().__init__()(除非它继承自其他类),否则会向上触达object.__init__并可能引发参数错误
MRO 不是运行时推导,而是在类创建时静态计算并冻结
__mro__ 在类定义完成那一刻就确定了,后续无法修改。这意味着:
- 动态添加父类(如通过
type(..., (B, C), {...}))会重新触发 C3 计算,但已存在的类对象不受影响 - 不能在运行时“切换”解析顺序;所谓“调整继承顺序”,本质是定义一个新类
- 调试时直接看
D.mro()比猜逻辑更可靠——尤其当继承链涉及第三方库的 Mixin 类时,它们的 MRO 可能和直觉相反
最易被忽略的一点:MRO 的合法性检查发生在类定义阶段,而不是方法调用时。一旦类成功创建,它的 __mro__ 就绝对稳定;但如果你依赖某个父类的特定位置做条件判断(比如 if B in D.__mro__[:3]),这种硬编码极易在重构继承结构后悄然失效。


















