>>>PyPathPython 学习站
首页›核心篇›kp-018
核心篇 · Core lv.2 核心 kp-018

继承与 MRO

前置知识:kp-017

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. 常见误区

  1. "super() 调用父类" —— 调用的是 MRO 下一个类;单继承下恰好等于父类,多继承下不是。
  2. 忘记在 __init__ 里调 super().__init__(...) —— 父类初始化逻辑丢失(属性缺失、混入失效)。
  3. 深继承树 —— 超过两三层的继承链基本是设计问题;优先组合。
  4. __mro__ 当成实现细节不敢看 —— Cls.__mro__、help(Cls) 随时可查,排查多继承问题先看它。

10. 自测题

  1. 上文 C(A, B) 场景中,为什么 Base 的 __init__ 只执行一次?
  2. class M(SaveMixin, ReadMixin, Base): 中 MRO 是什么顺序?
  3. 什么时候该用继承、什么时候用组合?
参考答案
  1. C3 线性化保证每个类在 MRO 中只出现一次;每个 super() 走链上下一位,最终恰好经过 Base 一次。
  2. M → SaveMixin → ReadMixin → Base → object(声明序优先)。
  3. is-a 且想复用/替换行为时继承;否则(has-a、只想用功能)组合——组合可测试性更好、耦合更低。

11. 与其他知识点的关系

  • kp-027 元编程:MRO 由类创建时的 type 逻辑生成。
  • kp-026 类型注解:Protocol 提供了"不继承也能多态"的替代路径。
  • kp-045 工程规范:Mixin 命名与设计惯例。

12. 延伸阅读

  • Python 官方文档:__mro__、super() 语义
  • Fluent Python 第 12 章