Python 多重继承与 MRO

说明 Python 多重继承为什么存在,MRO 如何决定属性查找顺序,以及 super 如何沿 C3 线性化后的顺序协作。

#type / concept #status / growing #resource / python #tech / lang / python

[!info] related notes

Python 多重继承与 MRO

一句话定义

多重继承允许一个类同时继承多个父类,而 MRO(Method Resolution Order,方法解析顺序)负责把“应该先找谁、再找谁”线性化成一条稳定顺序。

核心机制 / 工作原理

1. 多重继承解决的是“组合多种正交能力”

Python 允许:

class Child(Base1, Base2):
    ...

这意味着 Child 既能复用 Base1,也能复用 Base2。最常见的合理用途不是把多个“大而全的业务父类”乱堆在一起,而是:

  • 一个主基类提供核心语义
  • 若干 mixin 提供正交能力,例如日志、序列化、缓存、权限检查

2. 问题不在“能继承几个”,而在“查找顺序怎么定”

ChildBase1Base2 里都定义了同名方法时,Python 必须回答:

  • 先调用哪一个
  • 下一个该轮到谁
  • 菱形继承里公共祖先是否会被重复执行

这就是 MRO 要解决的问题。

3. Python 用 C3 线性化生成 MRO

你可以先记住 C3 的三个目标:

  1. 保留子类声明的局部顺序,例如 Child(Base1, Base2) 表示 Base1 优先于 Base2
  2. 保留每个父类内部原有的继承顺序
  3. 同一个类在线性结果里只出现一次

最终 Python 会得到一条形如:

Child -> Base1 -> Base2 -> CommonBase -> object

这样的线性链,属性查找和 super() 都沿这条链工作。

4. super() 在多重继承里依赖这条线性链协作

只要每个类都调用 super(),公共祖先就能按 MRO 恰好执行一次,而不会因为菱形结构被重复调用。

最小例子 / 最小场景

class A:
    def ping(self):
        print("A")


class B(A):
    def ping(self):
        print("B start")
        super().ping()
        print("B end")


class C(A):
    def ping(self):
        print("C start")
        super().ping()
        print("C end")


class D(B, C):
    pass


D().ping()
print(D.__mro__)

输出顺序会体现协作链:

B start
C start
A
C end
B end

因为 D 的 MRO 大致是 D -> B -> C -> A -> object,所以 super() 不是“回父类”,而是“沿 MRO 继续向后走”。

边界与易混淆点

  1. 多重继承不是默认首选;如果只是把两个对象拼起来协作,组合通常更简单。
  2. 真正适合多重继承的是 mixin 风格的“可叠加能力”,而不是多个互相强耦合的具体父类。
  3. 只要某个类跳过 super(),整条协作链就可能断掉;这也是为什么直接写死 Base.method(self) 很危险。
创建于 2026/5/20 更新于 2026/5/27