1. 引言:为什么需要 MRO
在 Python 中,一个类可以同时继承多个父类,这就是所谓的多重继承。当子类调用某个方法时,如果这个方法在多个父类中都存在,Python 到底该用哪一个?
python
class A:
def hello(self):
print("A.hello")
class B:
def hello(self):
print("B.hello")
class C(A, B):
pass
C().hello() # 输出什么?
答案是 A.hello。这个"选择顺序"就是 MRO(Method Resolution Order,方法解析顺序)。
MRO 决定了三件事:
- 属性查找顺序 :
obj.attr时按什么顺序在类及其父类中查找; - 方法调用顺序 :
super()到底调用的是谁; - 多继承的合法性:某些继承结构 Python 会直接拒绝创建类。
理解 MRO,是写出可维护多重继承代码的前提,也是面试中高频考察的知识点。
2. MRO 的直观理解
2.1 一个简单的多继承例子
python
class A:
def who(self):
print("A")
class B(A):
def who(self):
print("B")
class C(A):
def who(self):
print("C")
class D(B, C):
pass
print(D.__mro__)
输出:
text
(<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
可以看到,D 的查找顺序是 D → B → C → A → object。这就是一个典型的菱形继承结构:
#mermaid-svg-5Epmj2SaoMpI5fdr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5Epmj2SaoMpI5fdr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5Epmj2SaoMpI5fdr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5Epmj2SaoMpI5fdr .error-icon{fill:#552222;}#mermaid-svg-5Epmj2SaoMpI5fdr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5Epmj2SaoMpI5fdr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5Epmj2SaoMpI5fdr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5Epmj2SaoMpI5fdr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5Epmj2SaoMpI5fdr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5Epmj2SaoMpI5fdr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5Epmj2SaoMpI5fdr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5Epmj2SaoMpI5fdr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5Epmj2SaoMpI5fdr .marker.cross{stroke:#333333;}#mermaid-svg-5Epmj2SaoMpI5fdr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5Epmj2SaoMpI5fdr p{margin:0;}#mermaid-svg-5Epmj2SaoMpI5fdr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5Epmj2SaoMpI5fdr .cluster-label text{fill:#333;}#mermaid-svg-5Epmj2SaoMpI5fdr .cluster-label span{color:#333;}#mermaid-svg-5Epmj2SaoMpI5fdr .cluster-label span p{background-color:transparent;}#mermaid-svg-5Epmj2SaoMpI5fdr .label text,#mermaid-svg-5Epmj2SaoMpI5fdr span{fill:#333;color:#333;}#mermaid-svg-5Epmj2SaoMpI5fdr .node rect,#mermaid-svg-5Epmj2SaoMpI5fdr .node circle,#mermaid-svg-5Epmj2SaoMpI5fdr .node ellipse,#mermaid-svg-5Epmj2SaoMpI5fdr .node polygon,#mermaid-svg-5Epmj2SaoMpI5fdr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5Epmj2SaoMpI5fdr .rough-node .label text,#mermaid-svg-5Epmj2SaoMpI5fdr .node .label text,#mermaid-svg-5Epmj2SaoMpI5fdr .image-shape .label,#mermaid-svg-5Epmj2SaoMpI5fdr .icon-shape .label{text-anchor:middle;}#mermaid-svg-5Epmj2SaoMpI5fdr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5Epmj2SaoMpI5fdr .rough-node .label,#mermaid-svg-5Epmj2SaoMpI5fdr .node .label,#mermaid-svg-5Epmj2SaoMpI5fdr .image-shape .label,#mermaid-svg-5Epmj2SaoMpI5fdr .icon-shape .label{text-align:center;}#mermaid-svg-5Epmj2SaoMpI5fdr .node.clickable{cursor:pointer;}#mermaid-svg-5Epmj2SaoMpI5fdr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5Epmj2SaoMpI5fdr .arrowheadPath{fill:#333333;}#mermaid-svg-5Epmj2SaoMpI5fdr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5Epmj2SaoMpI5fdr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5Epmj2SaoMpI5fdr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Epmj2SaoMpI5fdr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5Epmj2SaoMpI5fdr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Epmj2SaoMpI5fdr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5Epmj2SaoMpI5fdr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5Epmj2SaoMpI5fdr .cluster text{fill:#333;}#mermaid-svg-5Epmj2SaoMpI5fdr .cluster span{color:#333;}#mermaid-svg-5Epmj2SaoMpI5fdr div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5Epmj2SaoMpI5fdr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5Epmj2SaoMpI5fdr rect.text{fill:none;stroke-width:0;}#mermaid-svg-5Epmj2SaoMpI5fdr .icon-shape,#mermaid-svg-5Epmj2SaoMpI5fdr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Epmj2SaoMpI5fdr .icon-shape p,#mermaid-svg-5Epmj2SaoMpI5fdr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5Epmj2SaoMpI5fdr .icon-shape .label rect,#mermaid-svg-5Epmj2SaoMpI5fdr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Epmj2SaoMpI5fdr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5Epmj2SaoMpI5fdr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5Epmj2SaoMpI5fdr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} A
B
C
D
2.2 两个核心约束
MRO 并不是随便排的,它必须同时满足两个约束:
- 局部优先顺序 :子类永远排在父类前面;在
class D(B, C)中,B必须排在C前面。 - 单调性:如果某个类在 MRO 中排在另一个类前面,那么在所有父类的 MRO 中也要保持这个相对顺序。
正是这两条约束,让 Python 2.3 之后采用了 C3 线性化算法。
3. C3 线性化算法详解
3.1 算法定义
C3 算法用递归方式定义类 C 的 MRO:
text
L[C] = C + merge(L[B1], L[B2], ..., L[Bn], [B1, B2, ..., Bn])
其中 B1...Bn 是 C 的直接父类,merge 是一个特殊的合并操作。
merge 的规则是:
- 依次取出列表的第一个元素作为候选;
- 如果该候选没有出现在其他任何列表的尾部(即不是别人的"后继"),则把它取出,并从所有列表中删除;
- 如果候选出现在某个列表的尾部,则跳过,看下一个列表的第一个元素;
- 重复直到所有列表为空;若无法继续,说明继承关系存在冲突,抛出
TypeError。
3.2 手动推演一个例子
还是上面的菱形继承:
text
L[A] = [A, object]
L[B] = [B] + merge(L[A], [A]) = [B, A, object]
L[C] = [C] + merge(L[A], [A]) = [C, A, object]
L[D] = [D] + merge(L[B], L[C], [B, C])
计算 merge([B, A, object], [C, A, object], [B, C]):
| 步骤 | 候选 | 是否在尾部 | 结果 |
|---|---|---|---|
| 1 | B | 否(B 只在 [B, C] 头部) |
取出 B |
| 2 | A | 是(在 [C, A, object] 尾部) |
跳过 |
| 3 | C | 否 | 取出 C |
| 4 | A | 否 | 取出 A |
| 5 | object | 否 | 取出 object |
最终:
text
L[D] = [D, B, C, A, object]
与 D.__mro__ 的输出完全一致。
3.3 用代码验证
python
def c3_mro(cls):
"""简化版 C3 线性化实现,用于验证"""
bases = cls.__bases__
if not bases:
return [cls]
seqs = [c3_mro(b) for b in bases] + [list(bases)]
result = [cls]
while True:
seqs = [s for s in seqs if s]
if not seqs:
return result
for seq in seqs:
head = seq[0]
if not any(head in s[1:] for s in seqs):
break
else:
raise TypeError(f"Cannot create a consistent MRO for {cls}")
result.append(head)
for s in seqs:
if s and s[0] is head:
s.pop(0)
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(c3_mro(D) == list(D.__mro__)) # True
4. 冲突检测:什么时候会报错
C3 算法的一个重要作用是提前发现不合法的继承结构。
4.1 经典冲突案例
python
class X: pass
class Y: pass
class A(X, Y): pass
class B(Y, X): pass
class C(A, B): pass # TypeError
报错信息:
text
TypeError: Cannot create a consistent method resolution order (MRO) for bases X, Y
原因分析:
A要求X在Y前;B要求Y在X前;C(A, B)同时继承两者,无法同时满足,冲突。
4.2 冲突的本质
冲突的本质是偏序关系出现环 。C3 要求所有约束构成一个可拓扑排序的有向无环图,一旦出现 X < Y 和 Y < X 同时成立,就无法线性化。
4.3 如何排查
遇到这类报错时,可以逐个检查父类的 MRO:
python
print(A.__mro__)
print(B.__mro__)
对比两者对共同祖先的相对顺序,通常就能定位冲突来源。实际工程中,这类问题多出现在混入(Mixin)组合场景,尤其是多个第三方库的基类被同时继承时。
5. super() 与 MRO 的协作机制
5.1 super() 不是"调用父类"
很多人误以为 super() 就是调用父类方法,实际上它调用的是 MRO 中当前类的下一个类。
python
class A:
def hello(self):
print("A")
class B(A):
def hello(self):
print("B")
super().hello()
class C(A):
def hello(self):
print("C")
super().hello()
class D(B, C):
def hello(self):
print("D")
super().hello()
D().hello()
输出:
text
D
B
C
A
注意:B 中的 super().hello() 调用的是 C,而不是 A!因为 D 的 MRO 是 D → B → C → A,B 的下一个就是 C。
5.2 协作式多继承
这种机制被称为协作式多继承(cooperative multiple inheritance) :每个类只负责自己那部分逻辑,然后通过 super() 把控制权交给链上的下一个类。
python
class LoggingMixin:
def process(self, data):
print(f"[LOG] processing {data}")
return super().process(data)
class ValidationMixin:
def process(self, data):
if not data:
raise ValueError("empty data")
return super().process(data)
class Base:
def process(self, data):
return data.upper()
class Service(LoggingMixin, ValidationMixin, Base):
pass
print(Service().process("hello"))
输出:
text
[LOG] processing hello
HELLO
Service 的 MRO 是 Service → LoggingMixin → ValidationMixin → Base → object,super() 沿着这条链依次传递,每个 Mixin 只做自己的事。
5.3 关键细节:super() 的隐式参数
super() 在方法内部使用时,等价于 super(__class__, self),其中 __class__ 是定义该方法的类,而不是调用时的类。这正是协作式多继承能工作的根本原因。
python
class B(A):
def hello(self):
# 这里的 super() 等价于 super(B, self)
# 即使 self 是 D 的实例,也从 B 之后开始查找
super().hello()
6. 实战中的常见陷阱
6.1 忘记调用 super()
python
class Base:
def __init__(self):
self.value = 1
class Mixin:
def __init__(self):
self.extra = 2
# 忘记 super().__init__()
class Child(Mixin, Base):
pass
c = Child()
print(hasattr(c, "value")) # False
Mixin.__init__ 没有调用 super().__init__(),导致 Base.__init__ 从未执行。协作式多继承要求链上每个类都调用 super()。
6.2 参数传递不一致
python
class A:
def __init__(self, **kwargs):
super().__init__(**kwargs)
class B:
def __init__(self, name, **kwargs):
self.name = name
super().__init__(**kwargs)
class C(A, B):
pass
C(name="test") # 正常工作
推荐所有参与协作的类都使用 **kwargs 透传,避免参数在链上丢失。
6.3 直接调用父类方法破坏 MRO
python
class B(A):
def hello(self):
A.hello(self) # 硬编码,绕过了 MRO
这种写法在单继承下没问题,但在多继承中会重复调用 或跳过某些类 ,破坏协作链。应始终使用 super()。
6.4 混入类的顺序影响行为
python
class Service(LoggingMixin, ValidationMixin, Base): ...
class Service2(ValidationMixin, LoggingMixin, Base): ...
两者的 MRO 不同,process 的执行顺序也不同。Mixin 的书写顺序就是执行顺序,需要根据业务语义谨慎排列。
7. 查看与分析 MRO 的工具
7.1 内置属性
python
class D(B, C): pass
print(D.__mro__) # 元组形式
print(D.mro()) # 列表形式,可被子类重写
print(D.__bases__) # 直接父类
7.2 可视化 MRO
python
def print_mro(cls, indent=0):
print(" " * indent + cls.__name__)
for base in cls.__bases__:
print_mro(base, indent + 1)
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print_mro(D)
输出:
text
D
B
A
C
A
7.3 用 mermaid 画出继承与 MRO 的关系
#mermaid-svg-5MzbT0U1Qs7hWbQP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5MzbT0U1Qs7hWbQP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5MzbT0U1Qs7hWbQP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5MzbT0U1Qs7hWbQP .error-icon{fill:#552222;}#mermaid-svg-5MzbT0U1Qs7hWbQP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5MzbT0U1Qs7hWbQP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5MzbT0U1Qs7hWbQP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5MzbT0U1Qs7hWbQP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5MzbT0U1Qs7hWbQP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5MzbT0U1Qs7hWbQP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5MzbT0U1Qs7hWbQP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5MzbT0U1Qs7hWbQP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5MzbT0U1Qs7hWbQP .marker.cross{stroke:#333333;}#mermaid-svg-5MzbT0U1Qs7hWbQP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5MzbT0U1Qs7hWbQP p{margin:0;}#mermaid-svg-5MzbT0U1Qs7hWbQP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5MzbT0U1Qs7hWbQP .cluster-label text{fill:#333;}#mermaid-svg-5MzbT0U1Qs7hWbQP .cluster-label span{color:#333;}#mermaid-svg-5MzbT0U1Qs7hWbQP .cluster-label span p{background-color:transparent;}#mermaid-svg-5MzbT0U1Qs7hWbQP .label text,#mermaid-svg-5MzbT0U1Qs7hWbQP span{fill:#333;color:#333;}#mermaid-svg-5MzbT0U1Qs7hWbQP .node rect,#mermaid-svg-5MzbT0U1Qs7hWbQP .node circle,#mermaid-svg-5MzbT0U1Qs7hWbQP .node ellipse,#mermaid-svg-5MzbT0U1Qs7hWbQP .node polygon,#mermaid-svg-5MzbT0U1Qs7hWbQP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5MzbT0U1Qs7hWbQP .rough-node .label text,#mermaid-svg-5MzbT0U1Qs7hWbQP .node .label text,#mermaid-svg-5MzbT0U1Qs7hWbQP .image-shape .label,#mermaid-svg-5MzbT0U1Qs7hWbQP .icon-shape .label{text-anchor:middle;}#mermaid-svg-5MzbT0U1Qs7hWbQP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5MzbT0U1Qs7hWbQP .rough-node .label,#mermaid-svg-5MzbT0U1Qs7hWbQP .node .label,#mermaid-svg-5MzbT0U1Qs7hWbQP .image-shape .label,#mermaid-svg-5MzbT0U1Qs7hWbQP .icon-shape .label{text-align:center;}#mermaid-svg-5MzbT0U1Qs7hWbQP .node.clickable{cursor:pointer;}#mermaid-svg-5MzbT0U1Qs7hWbQP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5MzbT0U1Qs7hWbQP .arrowheadPath{fill:#333333;}#mermaid-svg-5MzbT0U1Qs7hWbQP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5MzbT0U1Qs7hWbQP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5MzbT0U1Qs7hWbQP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5MzbT0U1Qs7hWbQP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5MzbT0U1Qs7hWbQP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5MzbT0U1Qs7hWbQP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5MzbT0U1Qs7hWbQP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5MzbT0U1Qs7hWbQP .cluster text{fill:#333;}#mermaid-svg-5MzbT0U1Qs7hWbQP .cluster span{color:#333;}#mermaid-svg-5MzbT0U1Qs7hWbQP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5MzbT0U1Qs7hWbQP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5MzbT0U1Qs7hWbQP rect.text{fill:none;stroke-width:0;}#mermaid-svg-5MzbT0U1Qs7hWbQP .icon-shape,#mermaid-svg-5MzbT0U1Qs7hWbQP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5MzbT0U1Qs7hWbQP .icon-shape p,#mermaid-svg-5MzbT0U1Qs7hWbQP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5MzbT0U1Qs7hWbQP .icon-shape .label rect,#mermaid-svg-5MzbT0U1Qs7hWbQP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5MzbT0U1Qs7hWbQP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5MzbT0U1Qs7hWbQP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5MzbT0U1Qs7hWbQP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MRO 顺序
D
B
C
A
object
继承关系
D
B
C
A
8. 总结
| 要点 | 说明 |
|---|---|
| MRO 定义 | 方法/属性的查找顺序 |
| 算法 | C3 线性化,Python 2.3 起采用 |
| 核心约束 | 子类优先、单调性 |
| 冲突检测 | 偏序成环时抛 TypeError |
| super() 语义 | 调用 MRO 中当前类的下一个类 |
| 协作式多继承 | 链上每个类都调用 super() 并透传参数 |
| 常见陷阱 | 忘记 super、参数不一致、硬编码父类调用 |
一句话总结:MRO 是 Python 多重继承的"交通规则",C3 算法保证了这条规则的一致性和可预测性,而 super() 则是沿着这条规则行驶的车辆。
掌握 MRO,你就能放心地使用 Mixin 组合、理解框架源码中的继承结构,并在遇到 TypeError: Cannot create a consistent MRO 时快速定位问题。