在 Python 的继承体系中,super() 是用来调用父类方法的工具,但它的工作机制远比“直接调用父类”更精巧。理解它,是写出健壮多继承代码的关键。
super() 的本质
super() 并不返回父类对象,而是返回一个代理对象,它会按照方法解析顺序(MRO)去查找下一个类。
- 语法:
super().init()或super(当前类, self).method() - 在 Python 3 中,实例方法里可以直接写
super().method(),解释器会自动补全参数。 - 它依赖于两个核心信息:当前类(从哪里开始往上找)和实例(确定 MRO 的所属类)。
MRO(方法解析顺序)决定调用顺序
MRO 规定了多继承时,属性和方法的查找顺序。Python 使用 C3 线性化算法 计算 MRO,保证结果满足:
- 子类优先于父类
- 多个父类按定义时的书写顺序
- 所有父类都位于子类之后,且不重复
查看 MRO:
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.__mro__)
# (D, B, C, A, object)
调用 super() 时,就从当前类在 MRO 中的位置,往后找下一个类的方法。
为什么用 super() 而不是直接写父类名
直接写 Parent.init(self) 有三个问题:
- 硬编码父类名,一旦继承关系改变,需要逐一修改,不利于代码复用。
- 多继承时不能保证所有父类的方法都被正确调用——某个父类可能被遗漏或不恰当的多次调用。
- 破坏协作式调用链:在多继承下,
super()可以串联起一条完整的初始化链,而显式的父类调用做不到。
经典例子:钻石继承与 super() 的协作
class A:
def __init__(self):
print("A")
super().__init__()
class B(A):
def __init__(self):
print("B")
super().__init__()
class C(A):
def __init__(self):
print("C")
super().__init__()
class D(B, C):
def __init__(self):
print("D")
super().__init__()
D()
# 输出:D B C A
解释:D 的 MRO 是 D -> B -> C -> A -> object。每个类的 init 中都调用 super().init(),从而沿着 MRO 顺序传递,A 只被调用一次,且顺序符合预期。这种模式称为“协作式多继承”,要求所有参与者都使用 super()。
注意事项
- 必须保证
super()调用的方法存在:如果链条中某一环没有定义要调用的方法,会引发AttributeError。 - 不要忘记传参数:当
super()调用init时,如果父类需要额外参数,需要传递。通常建议在参数设计上保持兼容,或者用**kwargs灵活传递。 - 慎用
super(cls, obj)的形式:它允许你跳过 MRO 中的某些类,但这通常是危险信号,可能破坏预期行为。
实用总结
- 单继承场景下,
super()比硬编码父类名更灵活,修改父类不用改子类代码。 - 多继承场景下,所有类都应该使用
super()并保持调用链,不要混用显式父类调用,否则会破坏 MRO 链条。 super()不是“调用父类”,而是“调用 MRO 中的下一个类”。牢记这点,才能理解它在多继承中的真正价值。