python 的 mro 由 c3 线性化算法计算得出,确保子类在父类前、各父类声明顺序尽量保持,并使共同祖先尽可能靠后;若无法满足所有约束则报 typeerror。

Python 多继承中 mro() 返回的顺序怎么算出来的
Python 的 MRO(Method Resolution Order)不是按继承语句从左到右简单拼接,也不是按深度优先回溯——它是用 C3 线性化算法算出来的,目标是保证两个关键性质:子类永远在父类之前,且各父类的相对顺序尽量保持声明顺序。
你调用 ClassName.mro() 看到的元组,就是这个算法的输出结果。它不依赖运行时行为,只由类定义时的继承结构决定。
-
object永远在最后(除非你手动改__mro__,但不建议) - 如果多个父类有共同祖先,C3 会把那个祖先“推到尽可能靠后”,但又不能破坏任一父类自己的 MRO 约束
- 一旦 C3 算法找不到满足所有约束的线性序列,Python 就直接报
TypeError: Cannot create a consistent method resolution order (MRO)
为什么 class D(B, C) 的 MRO 不等于 B.mro() + C.mro() 去重
直觉上想“先把 B 的链条列出来,再把 C 的补上没出现过的”,但 C3 要求每个类的局部 MRO(即它自己继承列表的线性化结果)都必须是最终 MRO 的子序列——这比简单拼接严格得多。
比如 class A: pass,class B(A): pass,class C(A): pass,class D(B, C): pass:
立即学习“Python免费学习笔记(深入)”;
print(D.mro()) # <class '__main__.D'>, # <class '__main__.B'>, # <class '__main__.C'>, # <class '__main__.A'>, # <class 'object'>
这里 A 出现在 B 和 C 之后,是因为 B.mro() 是 [B, A, object],C.mro() 是 [C, A, object],C3 合并时必须让 A 在两者之后,否则就违反了任一父类的局部顺序。
super() 调用时到底找的是 MRO 里的下一个类
super() 不是“找父类”,而是按当前类的 __mro__,从调用位置往后找第一个有该方法的类。所以它的行为完全取决于 MRO 顺序,而不是语法上写的父类是谁。
- 在
D中写super().__init__(),实际调用的是B.__init__()(因为B是D.mro()中D之后的第一个有__init__的类) - 如果
B里也写了super().__init__(),那就继续往后找——可能是C,也可能是A,取决于B.mro()中B后面是谁 - 漏掉
super()或写错参数(比如super().__init__(x)但C.__init__不接受x),就会中断链,后面的方法根本不会执行
多继承下容易踩的 MRO 相关坑
多数问题不是出在“看不懂 MRO”,而是没意识到 MRO 会悄悄改变控制流走向,尤其在混用 mixin、动态类生成或第三方库时。
- 写 mixin 类时,别假设自己的
__init__一定在某个父类之后被调;显式检查hasattr(super(), '__init__')或用try/except包裹super().__init__() - 用
type('Name', (B, C), {})动态建类时,MRO 一样走 C3,但调试时看不到源码,建议立刻打印cls.mro() - Django 的
Model和很多 ORM 库都重度依赖 MRO,如果你继承了多个带Meta或自定义__new__的类,TypeError: Cannot create a consistent MRO往往意味着某两个父类的继承树存在不可调和的祖先冲突
MRO 看似是个静态规则,但它真正起作用的地方,全在 super() 那一下跳转里——那一下跳去哪,没人能光看类定义猜准,得看 .mro() 输出。










