编程学习网 > 编程语言 > Python > Python super():你以为它只是"调父类",其实它在走一条链
2026
06-29

Python super():你以为它只是"调父类",其实它在走一条链


有写过这样的代码吧:

class Dog(Animal):
    def speak(self):
        super().speak()
        print("汪汪")

然后得出结论:super() 作用就是调父类的方法。

这个理解只对了一半,看到的了表面现象,但其实并没有get到本质。这个结论在单继承下凑合能用,但一旦碰到多继承,它可能会违反你的固有认知。

本文通过详实的示例来一探究竟,一旦理解掌握super()本质,你将超过90%Python程序员。

先看一个"诡异"的例子

三个类,每个 __init__ 都调用 super()

class A:
    def __init__(self):
        print("A")
        super().__init__()
class B:
    def __init__(self):
        print("B")
        super().__init__()
class C(A, B):
    def __init__(self):
        print("C")
        super().__init__()
c = C()

凭直觉,你也许会觉得输出是 C → AA 没父类就停了)。

实际输出:

C
A
B

在这个例子中,A 调了 super().__init__(),但跳到的并不是 A 的父类(object),而是 B

没有调取父类对应方法,为啥呢?其实更准确地说, super() 不找"父类",它找的是 MRO 上的下一个

什么是 MRO

MROMethod Resolution Order,方法解析顺序。

Python 在创建类的时候就已确定了一条查找链:当你在对象上调用方法时,Python 沿着这条链依次查找。

你可以直接看 MRO

print(C.__mro__)
# (<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>)

MRO链是 C → A → B → object

所以 super().__init__() C 里,找 MRO C 的下一个,是 A。在 A 里调 super().__init__(),找 A 的下一个,是 BB 的下一个才是 object

整条链走完,每个 __init__ 都被调了,无遗漏,无重复。

super() 不是"调我爸",而是"MRO链中的下一个"

MRO 怎么算出来的

不是随便排的。Python C3 线性化算法得出,规则很简单:

1先查子类,再查父类(子类优先)2同一层级的父类,按声明顺序排(class C(A, B) → A B 前面)3不能打破父类之间的相对顺序

拿刚才的例子讲:

•C 的父类声明顺序是 A, B → MRO A 必须在 B 前面•C A B 的子类 → C 必须在 A 前面•A B 的父类都是 object → object 排最后

所以结果是 C → A → B → object。没有歧义,没有冲突。

如果继承关系画出来有矛盾(比如两个父类有继承关系,且顺序又声明反了),Python 会直接报 TypeError,类不会创建成功:

class A:
    pass
class B(A):
    pass
class X(A, B):  # 如果 B A 的子类,但声明里 A 又在 B 前面
    pass         # TypeError: Cannot create a consistent method resolution order(MRO) for base A, B

Python 不会让你写出 MRO 算不出的类。要么能排出顺序,正常继承;要么直接报 TypeError

再看一个实用的场景

混入(Mixin)模式,这是多继承在 Python 里最常见的正当用法。

你有一个日志 Mixin 和一个权限 Mixin

class LogMixin:
    def save(self):
        print(f"保存记录{self.data}")
        super().save()
class AuthMixin:
    def save(self):
        if not self.is_admin:
            raise PermissionError("没权限")
        super().save()
class BaseModel:
    def save(self):
        print("写入数据库")
class Product(LogMixin, AuthMixin, BaseModel):
    def __init__(self, data, is_admin):
        self.data = data
        self.is_admin = is_admin
p = Product("Huawei Mate 80", True)

调用 p.save()

1Product 对象 p 没有 save → MRO 下一个2LogMixin.save → 打印日志,调 super()3AuthMixin.save → 校验权限,调 super()4BaseModel.save → 写入数据库

MRO  Product → LogMixin → AuthMixin → BaseModel → object

每个 Mixin 只做自己的事,然后 super() 把接力棒传给下一个。像流水线:日志 权限 存储,自动串起来了。

这才是 super() + MRO 的正确打开方式:不是"调我爸",是"传给流水线下一站"

常见踩坑

1. 跳过了中间环节

有人觉得 Mixin 不重要,直接硬调某个父类:

class MyProduct(LogMixin, AuthMixin, BaseModel):
    def save(self):
        BaseModel.save(self)  # 直接调 BaseModel,跳过了 LogMixin AuthMixin

日志记录、权限校验被绕过了。super() 的设计意图就是让每个人都按 MRO 顺序走,不能跳过某个节点。

2. 没有接 super() 的接力棒

class LogMixin:
    def save(self):
        print("记录日志")
        # 没调 super()!接力棒断了

后面所有 Mixin BaseModel save 全不会被调用。链在这里断了。

Mixin 的时候,如果你参与了某个方法链,一定要调 super(),哪怕你不知道下一个是谁。这正是 super() 的意义——你不需要知道下一个是谁,它自己会找。

3. MRO 顺序跟直觉不符

class C(B, A):  # 注意,与前文中示例 class C(A, B) 不一样,声明顺序反的
    pass
# MRO: C → B → A → object

 class C(A, B) 的顺序不同。声明顺序决定了同层级父类的优先级。如果你依赖了某个顺序但声明反了,方法调用就会走不一样的路径。

记住:声明顺序就是优先级顺序。

如何查 MRO

三种方式查看MRO,如下:

ClassName.__mro__        # 元组,完整链
ClassName.mro()          # 列表,完整链
obj.__class__.__mro__    # 从实例查,同ClassName.__mro__

写多继承的时候,先查 MRO,再写代码。调用链路会清晰很多。

总结

•super() 调的不是父类,是 MRO 链上的下一个。

子类先,父类后,同级类按照声明顺序决定优先级。按链走,不跳站,不断棒。

下次写多继承,先 print(ClassName.__mro__),看清楚MRO链再继续下一步。

that's all. 如果觉得本文有点意思,请转发给有需要的朋友。

以上就是“Python super():你以为它只是"调父类",其实它在走一条链的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。 

扫码二维码 获取免费视频学习资料

Python编程学习

查 看2022高级编程视频教程免费获取