核心篇 · Core
lv.2 核心
kp-018
继承与 MRO
1. 一句话定义
继承让子类获得父类的行为;Python 多继承的方法查找顺序由 MRO(Method Resolution Order,C3 线性化) 唯一确定,super() 按 MRO 委托——不是"调用父类"这么简单。
2. 为什么重要
super() 的常见误读("就是调爸爸")在多继承/混入(mixin)场景下会直接出错。MRO 是理解 Django ORM、各类 mixin 框架源码的必备知识。
3. 前置知识
kp-017(类与属性查找)。
4. 核心概念
- 语法:
class Child(Parent):;object是万物之根。 - MRO:
Child.__mro__给出确定的查找链(C3 线性化:子类优先、父类按声明序、保证每个类只出现一次)。 - super():按
__mro__找"当前类的下一个"类,而不是"父类"。 - 组合优于继承:
has-a用组合,is-a才用继承。
5. 原理与机制
python
class Base:
def __init__(self): print("Base")
class A(Base):
def __init__(self):
print("A"); super().__init__()
class B(Base):
def __init__(self):
print("B"); super().__init__()
class C(A, B):
def __init__(self):
print("C"); super().__init__()
C()
# 输出:C A B Base —— 注意 Base 只执行一次!
print([c.__name__ for c in C.__mro__])
# ['C', 'A', 'B', 'Base', 'object']关键:A 的 super().__init__() 没有直接调 Base,而是调 MRO 中 A 的下一个(B)。这就是混入能协作、菱形继承父类方法不被重复执行的原因。
6. 关键事实(模型/图示)
text
菱形: C
/ \
A B
\ /
Base
MRO: C → A → B → Base → object (每类恰一次)
super(C, self).__init__() == A.__init__ (MRO 中 C 的下一个)Mixin 惯例:混入类应当窄(只加一类能力)、不直接实例化、放在继承列表前面(优先级)。
7. 直观类比
MRO 像会签流程:C 收到任务先签(自己的 __init__),然后传给名单上的下一位(A),A 签完再传下一位(B),而不是"签完就寄回总部(Base)"。"名单"就是 __mro__,super() 只是"传给名单下一位"。
8. 实例与案例
python
# 典型 Mixin:给类加序列化能力
class JSONMixin:
def to_json(self):
import json
return json.dumps(self.__dict__, ensure_ascii=False)
class User(JSONMixin):
def __init__(self, name): self.name = name
User("蛇").to_json() # '{"name": "蛇"}'9. 常见误区
- "super() 调用父类" —— 调用的是 MRO 下一个类;单继承下恰好等于父类,多继承下不是。
- 忘记在
__init__里调super().__init__(...)—— 父类初始化逻辑丢失(属性缺失、混入失效)。 - 深继承树 —— 超过两三层的继承链基本是设计问题;优先组合。
__mro__当成实现细节不敢看 ——Cls.__mro__、help(Cls)随时可查,排查多继承问题先看它。
10. 自测题
- 上文
C(A, B)场景中,为什么 Base 的__init__只执行一次? class M(SaveMixin, ReadMixin, Base):中 MRO 是什么顺序?- 什么时候该用继承、什么时候用组合?
参考答案
- C3 线性化保证每个类在 MRO 中只出现一次;每个 super() 走链上下一位,最终恰好经过 Base 一次。
- M → SaveMixin → ReadMixin → Base → object(声明序优先)。
- is-a 且想复用/替换行为时继承;否则(has-a、只想用功能)组合——组合可测试性更好、耦合更低。
11. 与其他知识点的关系
12. 延伸阅读
- Python 官方文档:
__mro__、super()语义 - Fluent Python 第 12 章