这个案例能让你搞清楚Python的super函数在底层是如何工作的吗

访客 源码剖析 1

本文目录导读:

  1. 核心前提:super() 不是“父类”!
  2. 底层运作机制(Python 3 视角)
  3. 经典案例:菱形继承
  4. 核心结论:怎么看出底层原理?
  5. 底层本质(一句话总结)

这是一个非常好的问题。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(顶级父类),BC(继承 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 结束

核心结论:怎么看出底层原理?

  1. super() 看的是 MRO,不是继承树。
    在上面的例子中,B.do 里的 super().do() 没有 去调用 B 的直接父类 A,而是跳过了 A,去调用了 C,为什么?因为 MRO 说 CB 后面。

  2. super() 的第一个参数决定了“跳过谁”。
    D.do 中,我们写了 super(B, self).do(),这直接告诉 Python:从 MRO 中 B后面 开始找,所以跳过了 B,找到了 C
    如果D.do 里我们写 super().do()(即 super(D, self).do()),那么它会跳过 D,去找 B

  3. 合作式多重继承(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(方法解析顺序) 委托机制

抱歉,评论功能暂时关闭!