super() 不是”调用父类”,而是沿 MRO 查找下一个类的代理;多继承下的方法解析顺序由 C3 线性化决定。

super() 的真实语义

class Base:
    def greet(self):
        return "Base"
 
class Child(Base):
    def greet(self):
        return "Child -> " + super().greet()

super().greet() 的含义:从当前实例的 MRO 中、Child 之后的那个类开始查找 greet——它未必是 Child 的直接父类。MRO 在类创建时算好:__mro__ 是元组,cls.mro() 返回列表,二者内容相同。

C3 线性化与菱形继承

class A:
    def who(self): print("A")
 
class B(A):
    def who(self): print("B"); super().who()
 
class C(A):
    def who(self): print("C"); super().who()
 
class D(B, C):
    def who(self): print("D"); super().who()
 
D().who()
# D -> B -> C -> A,A 只执行一次
D.__mro__
# (D, B, C, A, object)

C3 线性化的核心保证:

  • 子类总排在父类之前
  • 多个父类保持定义时的相对顺序(B 在 C 前)
  • 每个类只出现一次

菱形结构中 B 里的 super().who() 调到的是 C 而非 A——super 跟着 MRO 走,不跟着血缘走。

多继承下的 init 协作调用

每个类只调用一次 super().__init__(),多个类串成协作链:

class LoggingMixin:
    def __init__(self, **kwargs):
        print("init logging")
        super().__init__(**kwargs)   # 参数继续传给 MRO 下一环
 
class Model(LoggingMixin):
    def __init__(self, name: str, **kwargs):
        super().__init__(**kwargs)   # 即使下一环是 object 也调用
        self.name = name

协作式多继承的约定:统一 **kwargs 签名 + 每层必调 super(),否则链条断裂,某个基类的初始化被静默跳过。

Mixin 模式

Mixin 是只提供一小块功能、从不单独实例化的辅助基类,命名以 Mixin 结尾:

class SaveMixin:
    def save(self, path): ...
 
class LoadMixin:
    def load(self, path): ...
 
class Session(SaveMixin, LoadMixin):   # 组合多种能力
    ...

惯例:Mixin 放继承列表最左,保证其方法先于具体基类被解析;Mixin 依赖宿主提供的属性应在文档中写明。

何时不用继承

  • 只想复用实现而非”是一个”——改用组合:持有实例、显式委托
  • 只要求对象有某方法——协议与抽象基类 的 Protocol 走结构化检查,无需继承
  • 需要在类创建期介入行为——那是 元类 metaclass 的领地

方法查找是 属性访问与查找 机制在类体系上的直接延伸;自定义 +/len() 等行为的入口见 特殊方法。