← 返回题目列表

Python 的 MRO 是什么?super() 在多继承中如何工作?

高频 困难 第 13 / 21 题 更新于 2026/07/27
MROsuper多继承C3 线性化

简化版

MRO 是方法解析顺序,决定 Python 在继承层级中查找属性和方法的顺序。Python 3 使用 C3 线性化生成 MRO;super() 不是简单调用父类,而是沿着当前类的 MRO 调用下一个类的方法。

详细版

示例:

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

class B(A):
    def run(self):
        print("B")
        super().run()

class C(A):
    def run(self):
        print("C")
        super().run()

class D(B, C):
    def run(self):
        print("D")
        super().run()

d = D()
d.run()
print(D.__mro__)

输出顺序:

D
B
C
A

D.__mro__ 类似:

(D, B, C, A, object)

所以 super() 会沿 MRO 找下一个类,而不是固定找语法上的直接父类。多继承中要让链条完整,相关类的方法签名要兼容,并且都要正确调用 super()

完整版教学

一、为什么需要 MRO

单继承时,方法查找很直观:

class B(A):
    pass

查找 B 的方法时,先找 B,再找 A

多继承就复杂了:

class D(B, C):
    pass

如果 BC 都继承自 A,查找顺序怎么排?A 会不会被调用两次?父类之间的相对顺序如何保持?

MRO 就是为了解决这些问题。

二、查看 MRO 的方式

可以用:

print(D.__mro__)

或者:

print(D.mro())

输出是一个类序列。Python 查找属性和方法时,就按这个序列从左到右找。

例如:

class A:
    value = "A"

class B(A):
    value = "B"

class C(A):
    value = "C"

class D(B, C):
    pass

print(D.value)  # B

因为 MRO 是 D -> B -> C -> A -> object,先在 B 找到了 value

三、C3 线性化要保证什么

Python 3 的 MRO 使用 C3 线性化。面试里不一定要手算复杂 MRO,但要知道它遵守几个原则:

  • 子类在父类前面;
  • 多继承列表中,左边父类优先于右边父类;
  • 如果多个父类共享同一个祖先,祖先只出现一次;
  • 保持继承关系的一致性,无法一致时会报错。

例如:

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

MRO 是:

D, B, C, A, object

A 不会因为 BC 都继承它就出现两次。

四、super() 不是“父类名”的替代

很多人把:

super().run()

理解成:

Parent.run(self)

这在单继承里看起来差不多,但多继承里会出错。

D(B, C) 中,B 里的 super().run() 调到的不是 A.run,而是 MRO 中 B 后面的 C.run

D -> B -> C -> A -> object

所以 super() 的真实含义是:从当前类在 MRO 中的位置开始,找下一个类。

五、协作式多继承

多继承中,如果每个类都想参与同一条调用链,就要使用协作式写法:

class A:
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

class B(A):
    def __init__(self, b=None, **kwargs):
        self.b = b
        super().__init__(**kwargs)

class C(A):
    def __init__(self, c=None, **kwargs):
        self.c = c
        super().__init__(**kwargs)

class D(B, C):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

这里每一层都把自己处理不了的参数继续往后传。这样 MRO 链才能完整执行。

六、什么时候避免多继承

多继承很强,但也容易复杂。

适合多继承的场景:

  • mixin 小能力组合;
  • 框架规定的扩展点;
  • 多个横向能力叠加。

不适合:

  • 多个父类都有复杂状态;
  • 父类初始化参数不兼容;
  • 继承层级很深;
  • 只是为了复用几行代码。

如果只是复用能力,组合通常更容易维护。

七、常见误区与追问

心法:super() 不是“调用父类”,而是“沿着当前类的 MRO 链找下一个实现”。多继承能不能稳,关键看每一层是否协作式调用。

  • 误区:super() 永远等价于直接写父类名。 直接写父类名会固定调用目标,可能跳过 MRO 链上的其他类;super() 会根据 MRO 找下一个类,更适合协作式多继承。
  • 追问:C3 线性化解决什么问题? 它把多继承图变成一个稳定线性顺序,同时尽量保持局部继承顺序和单调性,避免同一个祖先类被重复或混乱调用。
  • 误区:菱形继承一定会重复调用顶层父类。 如果所有类都正确使用 super(),Python 会沿 MRO 让每个类按顺序出现一次;手写父类名才更容易重复调用。
  • 追问:为什么 mixin 通常不要有复杂状态? mixin 适合提供小而独立的横向能力;如果它依赖复杂初始化参数,多继承链里的参数传递和初始化顺序会变脆。
  • 误区:只要能跑,多继承就没问题。 多继承的维护风险在后续扩展时暴露,尤其是父类参数不兼容、方法同名、状态初始化互相依赖时。
  • 追问:怎么调试 MRO?Class.__mro__Class.mro() 打印顺序,再检查每个类的方法是否调用了 super() 并正确透传参数。

八、加强记忆

MRO 决定方法查找顺序,Python 3 用 C3 线性化保证顺序一致;super() 沿 MRO 找下一个类,不是机械地找“亲爹”。多继承里要让每个类都配合 super(),否则调用链可能断掉。面试讲 MRO 时,最好配一个菱形继承例子。