直接访问__mro__可查看python类的方法解析顺序,它是一个只读元组,按c3算法线性化继承链,类创建时即确定;推荐用inspect.getmro()安全获取,支持类或实例输入。

直接访问 __mro__ 查看继承路径
Python 类的 __mro__(Method Resolution Order)是一个元组,按从左到右顺序列出类及其所有父类(含 object),就是实际的方法查找路径。它不是动态计算的,而是类创建时就确定好的,所以查一次就够了。
直接打印即可:
class A: pass class B(A): pass class C(A): pass class D(B, C): pass <p>print(D.<strong>mro</strong>)</p><h1>输出:(<class>main.D'>, <class>main.B'>, <class>main.C'>, <class>main.A'>, <class>)</class></class></class></class></class> </h1><p></p>
-
__mro__是只读属性,不能赋值或修改 - 必须通过类名访问(如
D.__mro__),实例对象上没有该属性 - 如果类使用了
__slots__或多重继承中存在不兼容的__init__签名,__mro__仍有效,但实际调用可能抛出TypeError
用 inspect.getmro() 安全获取
相比直接访问 __mro__,inspect.getmro() 更健壮——它接受类或实例,自动提取类并返回元组,对传入错误类型会报明确异常,而不是静默失败。
import inspect <p>print(inspect.getmro(D)) # 同 <strong>mro</strong> print(inspect.getmro(D())) # 传实例也行,自动取 type() </p>
- 推荐在工具函数或调试脚本中使用
inspect.getmro(),尤其当你不确定输入是类还是实例时 - 它不绕过 MRO 规则,只是封装了类型检查和提取逻辑
- 注意:它不会帮你格式化输出,如果要树状展示,还得自己处理缩进
可视化继承树:递归打印缩进结构
__mro__ 是线性元组,但人类更习惯看缩进树。可以用递归方式模拟 C3 算法的层级感(虽然不完全等价于真实解析过程,但对理解继承链足够直观):
def show_mro_tree(cls, indent=0):
print(" " * indent + cls.__name__)
for base in cls.__bases__:
show_mro_tree(base, indent + 1)
<p>show_mro_tree(D)</p><h1>输出:</h1><h1>D</h1><h1>B</h1><h1>A</h1><h1>C</h1><h1>A</h1><p></p>
- 这里用的是
__bases__而非__mro__,因为目标是“谁直接继承了谁”,不是“方法怎么找” - 重复出现的
A不代表有问题,这是多重继承的自然结果;__mro__的价值正在于消除了这种歧义,确保唯一查找顺序 - 如果类有
__slots__或使用了abc.ABCMeta,__bases__依然可靠,但要注意元类可能插入中间类(如ABCMeta自动加的ABC)
常见误判:把 __mro__ 当成继承图,忽略 C3 合并规则
看到 D.__mro__ 里 B 在 C 前面,就以为 “B 比 C 更优先”,其实这只是最终线性化结果。真实继承关系中,B 和 C 是平级父类,MRO 是 C3 算法合并多个父链后的产物。
- 当修改某个父类的继承顺序(比如让
C继承B),整个__mro__可能重排,不只是局部调整 -
super()调用严格依赖__mro__,不是靠代码书写顺序,也不是靠是否定义了同名方法 - 最易忽略的一点:自定义元类、描述符或
__getattribute__干预,不会改变__mro__,但会让方法实际调用路径“看起来”不一致——这时候该怀疑的是属性访问逻辑,而不是 MRO 本身
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











