__mro__是python类的只读元组,记录c3线性化算法确定的方法解析顺序,决定属性/方法查找时从左到右依次搜索的类序列,首项为自身、末项为object,直接影响super()调用路径和同名方法覆盖结果。

什么是 __mro__,它在方法查找中起什么作用?
__mro__ 是 Python 类的一个只读元组,记录了该类在多重继承下的方法解析顺序(Method Resolution Order)。当你调用一个实例方法时,Python 不是简单地从当前类向上逐级找父类,而是严格按 __mro__ 列出的顺序从左到右搜索——第一个定义了该方法的类就胜出。这意味着即使某个父类确实实现了方法,只要它在 __mro__ 中排得靠后,且左侧有其他类也提供了同名方法,它就会被跳过。
排查故障的核心就是:**方法没按你预期的方式被调用,往往是因为你误判了它实际来自哪个类**。直接打印 MyClass.__mro__ 能立刻暴露真实查找路径,比反复翻父类定义快得多。
如何快速定位“为什么调用的是 A 类的方法,而不是我以为的 B 类?”
典型场景:你写了一个子类 Child,继承自 ParentA 和 ParentB,但 Child().foo() 执行的是 ParentA.foo,而你期望它走 ParentB.foo。这时:
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
- 先执行
print(Child.__mro__),观察输出类似(<class>, <class>, <class>, <class>)</class></class></class></class> - 如果
ParentA确实在ParentB左侧,那结果就是符合规则的——不是 bug,是你对继承顺序的理解有偏差 - 检查是否意外触发了「菱形继承」:比如
ParentA和ParentB都继承自同一个基类Base,而Base也定义了foo,那么Base.foo可能因 C3 算法被提前,导致ParentB.foo根本不被看到 - 注意:
super()的行为也完全依赖__mro__;如果你在ParentA.foo中写了super().foo(),它会继续沿__mro__向后找下一个类,而非固定找ParentB
常见错误现象与对应检查点
以下现象几乎都可由 __mro__ 揭示根源:
-
AttributeError: 'X' object has no attribute 'method':该方法确实不在
X.__mro__中任何类里定义,或拼写不一致(比如大小写、下划线) -
方法被静默跳过,日志没输出:你在
ParentB.method里加了 print,但没看到——说明它根本没被调用,__mro__中它排在已实现该方法的类之后 -
多继承下
super().__init__()没按预期执行所有父类初始化:C3 算法保证每个类只初始化一次,但顺序由__mro__决定;若某父类构造器未被调用,大概率是它在__mro__中位置靠后,且前面的类已通过super()覆盖了链路 - 使用
type(obj).__mro__而非obj.__class__.__mro__—— 二者等价,但前者更直觉;避免误用obj.__mro__(实例没有这个属性)
兼容性与性能注意事项
__mro__ 自 Python 2.3 起就存在,所有现代版本行为一致。它是一个元组,生成开销极小,适合调试时临时打印,但不要在热路径中频繁访问(尽管实际影响微乎其微)。
真正容易被忽略的是:**__mro__ 只反映类定义时的继承结构,不随运行时 monkey patch 改变**。如果你动态给某个父类添加了方法,__mro__ 不会重排,但方法查找仍会生效——因为查找逻辑是在运行时按原顺序扫描各类字典,新方法只要在对应类的 __dict__ 里就能被找到。这点常让人误以为 __mro__ 失效了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










