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 的领地