本文目录导读:
这是一个非常好的问题。super() 是 Python 中一个经典且容易让人困惑的概念,但一旦理解了其底层的 MRO(方法解析顺序,Method Resolution Order) 和 合作式多重继承(Cooperative Multiple Inheritance),就会非常清晰。
我来通过一个经典且最具代表性的 “菱形继承” 案例,从底层代码逻辑(__thisclass__, __self__)和 MRO 计算两方面,帮你彻底搞清楚 super() 是如何工作的。
核心前提:super() 不是“父类”!
很多人以为 super() 调用父类的方法”。这是错的。
准确地说,super() 返回的是一个 代理对象,它负责根据 MRO 自动决定“下一个”该调用谁的函数,这个“下一个”不一定是父类,也可能是另一个继承链上的兄弟类。
底层运作机制(Python 3 视角)
当你写 super().method() 时,解释器实际上执行的是:
super(ClassA, self).method()
ClassA:表示 从哪个类开始 往后找(不包含自身)。self:提供 MRO 信息(即type(self).__mro__)。
核心逻辑:
super() 会直接去查看 self 的 MRO 列表,找到 ClassA 在 MRO 中的位置,然后返回该位置 下一个元素 的绑定方法。
经典案例:菱形继承
我们有四个类:A(顶级父类),B 和 C(继承 A),D(继承 B 和 C)。
class A:
def do(self):
print("A.do")
class B(A):
def do(self):
print("B.do 开始")
super().do() # 这里调用的不是 A.do!
print("B.do 结束")
class C(A):
def do(self):
print("C.do 开始")
super().do()
print("C.do 结束")
class D(B, C):
def do(self):
print("D.do 开始")
super(B, self).do() # 明确指定从 B 开始找下一个
print("D.do 结束")
调用 d = D(); d.do()
计算的 MRO
D 类的 MRO 是 Python 通过 C3 线性化算法 计算出的:
[D, B, C, A, object]
这是 super() 工作的“地图”。
一步一步的调用链(关键!)
第 1 步: d.do() 进入 D.do,打印 "D.do 开始"。
第 2 步: 执行 super(B, self).do()。
- 底层解析:
super(B, self)查看type(self).__mro__即[D, B, C, A, object],找到B的位置(索引 1),取下一个元素(索引 2):C。 - 结果: 它调用的是
C.do(),而不是 B 的父类 A.do()。
第 3 步: 进入 C.do,打印 "C.do 开始"。
第 4 步: 执行 C.do 中的 super().do()(等价于 super(C, self).do())。
- 底层解析: 在 MRO
[D, B, C, A, object]中找到C(索引 2),取下一个(索引 3):A。 - 结果: 调用
A.do(),打印"A.do"。
第 5 步: 回到C.do,打印"C.do 结束"。
第 6 步: 回到D.do中的super(B, self).do()之后,打印"D.do 结束"。
最终输出:
D.do 开始
B.do 开始
C.do 开始
A.do
C.do 结束
B.do 结束
D.do 结束
核心结论:怎么看出底层原理?
-
super()看的是 MRO,不是继承树。
在上面的例子中,B.do里的super().do()没有 去调用 B 的直接父类A,而是跳过了 A,去调用了C,为什么?因为 MRO 说C在B后面。 -
super()的第一个参数决定了“跳过谁”。
在D.do中,我们写了super(B, self).do(),这直接告诉 Python:从 MRO 中B的 后面 开始找,所以跳过了B,找到了C。
如果 在D.do里我们写super().do()(即super(D, self).do()),那么它会跳过D,去找B。 -
合作式多重继承(Cooperative Inheritance)
所有的do()方法都用了super().do(),这形成了一条 链条(D -> B -> C -> A),每个人都在完成自己的部分工作(print 开始/结束),然后调用下一个,最后回到自己。
如果任何一个类没有调用super(),链条就会断裂。
底层本质(一句话总结)
super() 是一个 MRO 的切片器。
它接收一个类 cls 和当前实例 instance,然后去 type(instance).__mro__ 这个元组里,返回 cls 后面那个类的绑定方法。
代码模拟底层逻辑(伪代码)
def my_super(cls, instance):
mro = type(instance).__mro__ # 获取 MRO 列表
idx = mro.index(cls) # 找到 cls 的位置
next_class = mro[idx + 1] # 取下一个类
# 返回这个类的实例绑定方法
return next_class.method(instance)
理解 super() 的关键在于 理解 MRO 和 那个“跳过自身,找下一个” 的机制,它从来不是简单的“找爸爸”,而是“在 MRO 这条单行线上,找紧跟着我的那一位”。
标签: MRO(方法解析顺序) 委托机制