
有写过这样的代码吧:
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 → A(A 没父类就停了)。
实际输出:
C
A
B
在这个例子中,A 调了 super().__init__(),但跳到的并不是 A 的父类(object),而是 B。
没有调取父类对应方法,为啥呢?其实更准确地说, super() 不找"父类",它找的是 MRO 上的下一个。
什么是 MRO
MRO,Method 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 的下一个,是 B。B 的下一个才是 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教程欢迎持续关注编程学习网。
扫码二维码 获取免费视频学习资料

- 本文固定链接: http://www.phpxs.com/post/14268/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料