Python 多继承的 MRO C3 算法原理是什么,super() 在多继承中如何工作?
多继承不是乱炖,Python 靠 C3 线性化 定规矩。下面用最精要的方式拆解,保证你听完就能讲给同事听。
🧬 MRO 是什么?¶
Method Resolution Order —— 方法解析顺序。
当你在 class D(B, C) 里调 self.method(),Python 必须知道先去 B 还是 C 找。
MRO 就是那个排好序的类列表,让每次查找都是确定的、无歧义的。
🔢 C3 算法原理(一句话版)¶
MRO = 自己 + 合并所有父类的 MRO + 父类本身
合并规则(merge)是精髓:
-
取第一个列表的头(第一个元素)
-
如果这个头没有出现在任何其他列表的尾部,就把它拿出来,并从所有列表中删除这个头
-
否则,看下一个列表的头
-
循环直到所有列表为空
-
如果找不到合法头 → 💥 抛出
TypeError,拒绝创建不一致的继承
公式感:
L[C] = C + merge(L[B1], L[B2], ..., B1, B2, ...)
经典钻石:
D(B, C) 且 B(A), C(A)
-
L[A] = A, object -
L[B] = B, A, object -
L[C] = C, A, object -
L[D] = D + merge(B,A,object , C,A,object , B,C)→ 头 B 不在其他尾部 → 取出 B → 下一头 C 不在其他尾部 → 取出 C → 然后 A、object 最终 MRO:D → B → C → A → object ✅
🚀 super() 的协同舞蹈¶
super() 不是直接调父类,而是沿着 MRO 链,找当前类后面的下一个类。
这造就了多重继承下“协作”模式:
class A:
def action(self):
print("A")
class B(A):
def action(self):
super().action()
print("B")
class C(A):
def action(self):
super().action()
print("C")
class D(B, C):
def action(self):
super().action()
print("D")
D().action() 输出顺序(MRO: D→B→C→A):
🔁 调用链是一根线穿到底:D 的 super 调 B,B 的 super 调 C,C 的 super 调 A,A 结束回溯。
这就是“协同”的意义:每个类只负责自己的事,把接力棒交给 MRO 链的下一个,完全不依赖具体父类是谁。
⚠️ 三个致命坑¶
1️⃣ 参数不匹配爆炸
如果 A.init(self, name) 需要参数,而 B、C 的 super 传了不同参数,直接在 C 那里崩掉。
✅ 解法:所有协作类都接受 **kwargs,用关键字参数透明传递。
2️⃣ 断链
B 调了 super() 但 C 没调,链就在 C 断了,A 永远收不到调用。
✅ 必须保证从叶子到根的所有类都用 super(),否则别混用。
3️⃣ 静态调用父类的陷阱
写 B.action(self) 硬编码会打乱 MRO,C 被跳过,导致钻石继承失效。
✅ 多继承下,禁止直接指名调用父类方法,一律走 super()。
🧠 面试加分点(直接让面试官画勾)¶
-
C3 的稳定性:保证“本地优先”和“单调性”(如果 B 在 A 前面,那么任何子类里 B 始终在 A 前)。这是 Python 2 经典类 DFS 所没有的。
-
super 的双面手:
super()不带参数返回 bound 对象,自动绑定了class和self,避免 Python 2 的super(D, self)重复。 -
工程实践:在 Mixin 模式中,Mixin 类必须放在“主继承链”前面,且都依赖 super() 协作。结合 Django 的
LoginRequiredMixin说明更香。
最后扔给你一道现考题:
把上面例子改成 class D(C, B),MRO 会变成什么?输出顺序又是什么?想通它,C3 和 super 你就算吃透了。