2025-10-06 Python不基础13——mro
文章目录
- 1. MRO定义与作用
- 1.1. 案例1:单继承
- 1.2. 案例2:多层单继承
- 1.3. 案例3:多继承
- 1.4. 案例4:复杂多继承
- 2. MRO两种获取方式
- 2.1. `__mro__`属性(返回元组)
- 2.2. `mro()`方法(返回列表)
- 3. C3线性化算法
- 3.1. 局部优先顺序(Local Precedence Order)
- 3.2. 单调性(Monotonicity)
- 3.3. 扩展优先图(Extended Precedence Graph)
- 4. MRO冲突
- 5. 总结
本文参考视频链接:
- https://www.bilibili.com/video/BV1V5411S7dY
MRO(Method Resolution Order,方法解析顺序)是Python解决多继承场景下方法/属性调用优先级的核心机制。在Python支持的多继承中,当子类调用一个方法/属性时,可能存在多个父类定义了该方法/属性——此时需要明确“优先从哪个父类查找”。
MRO的本质就是将子类的所有父类(包括间接父类)线性化成一个优先级队列,调用时按队列顺序依次查找,直到找到目标方法/属性。
1. MRO定义与作用
通过4个递进案例,直观理解MRO如何解决不同继承结构下的方法调用优先级问题。
1.1. 案例1:单继承
class A:def c(self):print("A")class M(A): # M仅继承A(单继承)passm = M()
m.c() # 调用结果:A
- 单继承下,M的MRO队列是
[M, A, object]
(object
是所有类的默认父类); - 调用
m.c()
时,按顺序先查M(无c
)→ 再查A(有c
)→ 执行A的c
,结果为“A”。
1.2. 案例2:多层单继承
class A:def c(self):print("A")class B(A): # B继承Adef c(self):print("B")class M(B): # M继承B(间接继承A)passm = M()
m.c() # 调用结果:B
- M的MRO队列是
[M, B, A, object]
; - 查找顺序:M(无)→ B(有
c
)→ 执行B的c
,结果为“B”(优先查直接父类,再查间接父类)。
1.3. 案例3:多继承
class A:def c(self):print("A")class B:def c(self):print("B")class M(A, B): # 继承顺序:A在前,B在后passm = M()
m.c() # 调用结果:A
class M(B, A): # 继承顺序:B在前,A在后passm = M()
m.c() # 调用结果:B
- 多继承下,MRO优先遵循“继承列表的顺序”(写在前面的父类优先级更高);
- 对应的MRO队列分别为
[M, A, B, object]
和[M, B, A, object]
,因此调用结果随继承顺序变化。
1.4. 案例4:复杂多继承
class A:def c(self):print("A")class C(A): # C继承Apassclass B: # B不继承Adef c(self):print("B")class M(C, B): # M继承C、Bpassm = M()
m.c() # 调用结果:A
class B(A): # 新增:B继承Adef c(self):print("B")class M(C, B): # 继承顺序不变:C在前,B在后passm = M()
m.c() # 调用结果:B
- 案例1的MRO队列:
[M, C, A, B, object]
→ 查M→C→A(有c
)→ 结果“A”; - 案例2的MRO队列:
[M, C, A, B, object]
?不,实际是[M, C, B, A, object]
(因B也继承A,C和B的共同父类是A,需调整顺序)→ 查M→C(无c
)→ B(有c
)→ 结果“B”; - 核心:复杂继承下,MRO不能仅看直接继承顺序,还需考虑间接父类的依赖关系,这就需要底层算法(C3)计算。
MRO作用
- 解决多继承歧义:明确方法/属性的查找顺序,避免“多个父类有同名方法时该调用哪个”的问题;
- 线性化继承关系:将“子类→直接父类→间接父类”的树形继承结构,转化为有序列表(MRO队列),查找时按列表顺序执行;
- 保证一致性:确保同一继承结构下,方法查找顺序唯一且符合直觉(由C3算法保证)。
2. MRO两种获取方式
Python为每个类提供了两种直接获取MRO队列的方式,无需手动计算,适用于任何继承结构。
2.1. __mro__
属性(返回元组)
- 语法:
类名.__mro__
; - 返回值:包含类本身、所有父类(直接/间接)、
object
的元组,顺序即MRO查找顺序。
print(M.__mro__)
# 输出:(<class '__main__.M'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
2.2. mro()
方法(返回列表)
- 语法:
类名.mro()
; - 返回值:与
__mro__
内容一致,但格式为列表(更易操作,如索引访问)。
print(M.mro())
# 输出:[<class '__main__.M'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
- 仅新式类(继承
object
的类,Python 3中所有类默认是新式类)支持这两种方式; - MRO队列的第一个元素永远是类本身,最后一个永远是
object
(所有类的顶层父类)。
3. C3线性化算法
Python 2.3版本后,MRO的计算统一采用C3线性化算法(C3 Linearization)。该算法由1996年提出,最初用于Dylan语言,核心是“在三个关键属性上保证一致性(Consistent)”,因此命名为“C3”。

C3算法通过严格的规则,确保MRO队列同时满足以下三个属性,从而避免歧义且符合直觉。视频从“易到难”拆解了这三个属性:

3.1. 局部优先顺序(Local Precedence Order)
- 子类继承多个父类时,继承列表中写在前面的父类,在MRO中优先级更高(对应案例3的“继承顺序影响结果”);
- 该优先级需“传递”给子类的子类:若父类A在父类B之前,则所有继承该子类的后代类,MRO中A的优先级仍高于B。
# 父类:A和B无继承
class A:def c(self):print("A")
class B:def c(self):print("B")# 子类M1:继承顺序A→B,A优先
class M1(A, B):pass
print(M1.mro()) # [M1, A, B, object]# 子类M2:继承M1,需保持A在B前
class M2(M1):pass
print(M2.mro()) # [M2, M1, A, B, object](A仍在B前)
- 关键结论:局部优先顺序是“写在前面即优先”,且该规则在继承链中不可打破。
3.2. 单调性(Monotonicity)
子类不能跳过直接父类,去调用间接父类的方法。即:若父类D的MRO中,方法c
来自父类X,则子类M(继承D)的MRO中,c
也必须来自X(或D本身),不能来自比X优先级更低的类。
# 继承结构:D继承C、B;C继承A;B无继承
class A:def c(self):print("A")
class C(A):pass
class B:def c(self):print("B")
class D(C, B): # D的MRO:[D, C, A, B, object],c来自Apass
class M(D): # M的直接父类是Dpass
print(M.mro()) # [M, D, C, A, B, object]
m = M()
m.c() # 结果:A(与D的c来源一致,未跳过D寻找B)
- 若M的MRO变为
[M, D, B, A, object]
,则M的c
来自B,而D的c
来自A——M跳过了D的方法来源(A),违反单调性。C3算法会避免这种情况。 - 单调性较难理解,实际编程中“只要不设计极端复杂的继承结构”,即可避免违反;若听不懂,可暂时聚焦“获取MRO结果”而非原理。
3.3. 扩展优先图(Extended Precedence Graph)
对于两个无直接继承关系的父类X和Y,若它们存在最小公共子类(即同时继承X和Y的最底层子类),则X和Y的优先级由“最小公共子类的继承顺序”决定。
图表案例
- 类结构:
D(B, A)
:继承顺序“B在前”;E(C, A)
:继承顺序“C在前”;M(D, E)
:最小公共子类(同时继承B和C的最底层类)。
- 优先级判断:
- 最小公共子类
M
的继承顺序是“D
在前,E
在后”; D
是B
的子类,E
是C
的子类;- 因此,在
M
的MRO中,B
的优先级高于C
。
- 最小公共子类
这是三个属性中最难的,核心是“通过最小公共子类的继承顺序,确定间接父类的优先级”,实际编程中极少需要手动分析,了解即可。
4. MRO冲突
当继承结构违反C3算法的规则时,Python会在编译阶段直接报错,无法生成MRO队列——因为不存在同时满足三个属性的线性化顺序。
冲突案例
# 矛盾的继承结构
class A:pass
class B:pass
class C(A, B): # C的MRO:[C, A, B, object],A优先于Bpass
class D(B, A): # D的MRO:[D, B, A, object],B优先于Apass
class M(C, D): # M同时继承C和D,冲突!passTypeError: Cannot create a consistent method resolution order (MRO) for bases A, B
冲突原因
- C要求“M的MRO中A在B前”,D要求“M的MRO中B在A前”;
- 两者矛盾,C3算法无法生成同时满足的MRO队列,因此Python直接拒绝创建类
M
。
5. 总结
- 定义:MRO是多继承下方法/属性的查找顺序,本质是“继承结构的线性化列表”;
- 获取:通过
类名.__mro__
(元组)或类名.mro()
(列表)直接获取; - 算法:Python 2.3+用C3线性化算法,确保MRO满足“局部优先、单调性、扩展优先图”三个属性;
- 冲突:矛盾的继承结构会导致MRO生成失败,Python直接报错;
- 实践:简化继承、查MRO结果、谨慎使用多继承,是避免MRO问题的关键。
通过理解MRO,可清晰解决多继承下的方法调用歧义,写出更健壮的Python类代码。
优先简化继承结构
- 多继承的复杂度与MRO的计算难度正相关,尽量采用“单继承+组合”替代多继承(如用Mixin类时,确保Mixin类功能单一,且继承顺序清晰);
- 例:若需同时实现“滚动”和“编辑”功能,优先用
class MyPane(B, C, Pane)
(Mixin类在前,主类在后),避免多层嵌套的交叉继承。
学会“查结果”而非“算过程”
- 无需手动计算MRO(C3算法复杂),编写继承代码后,用
__mro__
或mro()
查看实际查找顺序; - 例:定义类后先执行
print(类名.mro())
,确认方法查找顺序符合预期,再编写业务逻辑。
理性看待多继承
- 争议:部分程序员认为“多继承是原罪”(易导致MRO冲突和代码混乱),因此Java等语言仅支持单继承;
- Python态度:允许多继承,但需谨慎使用——合理的多继承(如功能单一的Mixin类)能提高代码复用,但滥用会导致维护困难;
- 核心原则:若使用多继承,确保“每个父类的职责明确”“继承顺序可解释”,且通过MRO验证无冲突。