飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

多重继承

Python 支持一个类同时继承多个基类。多重继承是强大的代码复用工具,但也引入了方法解析的复杂性。理解 MRO(Method Resolution Order)算法是正确使用多重继承的前提。

多继承语法

在类定义的圆括号中列出多个基类,即可实现多重继承:

class Base1:
    def method1(self):
        return 'Base1'

class Base2:
    def method2(self):
        return 'Base2'

class Derived(Base1, Base2):
    pass

d = Derived()
print(d.method1())   # Base1
print(d.method2())   # Base2

基类在圆括号中的顺序至关重要。当多个基类定义了同名方法时,Python 需要一套规则来决定调用哪一个。这套规则就是 MRO。

方法解析顺序(MRO)

Python 使用 C3 线性化算法 计算 MRO。该算法保证:

  1. 子类优先于父类:派生类总是出现在其基类之前
  2. 尊重声明顺序:基类在类头中的从左到右顺序被保留
  3. 单调性:一个类被继承时,不会影响其父类的优先顺序
  4. 每个类只出现一次:即使通过多条路径可达,类在 MRO 中仅出现一次

通过 __mro__ 属性或 mro() 方法可以查看计算结果:

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)

注意 B 排在 C 之前,因为 D(B, C) 的声明顺序是 B 在前。同时 A 只出现一次,尽管它同时是 B 和 C 的父类。

如果 C3 算法无法产生满足所有约束的线性顺序,Python 会抛出 TypeError:

class X:
    pass

class Y:
    pass

class Z(X, Y):
    pass

# class W(Y, X):       # 若取消注释,与 Z 冲突
#     pass

# class Bad(Z, W):      # TypeError: Cannot create a consistent method resolution
#     pass              # order (MRO) for bases X, Y

钻石问题

钻石问题(Diamond Problem)是多重继承的经典难题:当两个子类继承自同一个父类,而又有第四个子类同时继承这两个子类时,最顶层的父类通过两条路径被访问。

class Top:
    def method(self):
        return 'Top'

class Left(Top):
    def method(self):
        return 'Left'

class Right(Top):
    def method(self):
        return 'Right'

class Bottom(Left, Right):
    pass

b = Bottom()
print(b.method())          # Left —— 按 MRO,Left 先于 Right
print(Bottom.__mro__)
# (Bottom, Left, Right, Top, object)

Python 的 C3 MRO 确保 Top 只被访问一次,且位于 Left 和 Right 之后。这避免了某些语言中父类被重复初始化的问题。

super() 的协同调用

在多重继承中,super() 的行为与单继承有本质不同。它并不总是调用直接父类,而是按照 MRO 找到下一个类。

class Top:
    def __init__(self):
        print('Top init')
        super().__init__()

class Left(Top):
    def __init__(self):
        print('Left init')
        super().__init__()

class Right(Top):
    def __init__(self):
        print('Right init')
        super().__init__()

class Bottom(Left, Right):
    def __init__(self):
        print('Bottom init')
        super().__init__()

Bottom()
# Bottom init
# Left init
# Right init
# Top init

每个 super().__init__() 都沿着 MRO 链条调用下一个类的 __init__,最终 Top 只执行一次。这种设计被称为协同多重继承(cooperative multiple inheritance),要求继承链上的每个类都使用 super() 并遵循相同的函数签名约定。

如果某个中间类不使用 super(),链条会断裂:

class BrokenLeft(Top):
    def __init__(self):
        print('BrokenLeft init')
        # 没有调用 super()!

class Bottom2(BrokenLeft, Right):
    def __init__(self):
        print('Bottom2 init')
        super().__init__()

Bottom2()
# Bottom2 init
# BrokenLeft init
# Right 和 Top 的 __init__ 不会被执行!

混入类

混入类(Mixin)是一种设计模式:只提供特定功能片段、不打算独立实例化的小类。混入类通常不定义 __init__,只包含若干方法,通过多重继承组合到主类中。

class JSONSerializableMixin:
    """混入类:提供 JSON 序列化能力"""
    
    def to_json(self):
        import json
        return json.dumps(self.__dict__)

class ComparableMixin:
    """混入类:提供基于 __eq__ 的 != 运算符"""
    
    def __ne__(self, other):
        return not self.__eq__(other)

class Person(JSONSerializableMixin, ComparableMixin):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __eq__(self, other):
        if not isinstance(other, Person):
            return NotImplemented
        return self.name == other.name and self.age == other.age

p1 = Person('Alice', 30)
p2 = Person('Alice', 30)
print(p1.to_json())        # {"name": "Alice", "age": 30}
print(p1 != p2)            # False

混入类的命名约定通常以 Mixin 结尾,明确表明其用途。良好的混入类设计应当:

  • 不依赖主类的具体实现细节,只依赖约定接口
  • 不定义实例变量,或确保变量名不会与主类冲突
  • 使用 super() 协同调用,以便与其他混入类共存

多重继承的设计建议

多重继承虽然强大,但过度使用会导致代码难以追踪。实践中建议:

  1. 优先使用组合:"有一个"关系用组合,"是一个"关系才用继承
  2. 限制继承深度:继承层次超过三层时,MRO 往往难以直观推断
  3. 协同调用约定:设计可继承的类时,始终在 __init__ 中使用 super(),并接收 **kwargs 传递未消费的参数
  4. 抽象基类辅助:使用 abc.ABC 定义接口,强制子类实现约定方法,降低混入类的耦合风险
from abc import ABC, abstractmethod

class LoggerMixin(ABC):
    @abstractmethod
    def log_target(self):
        """子类必须返回日志输出目标"""
        pass
    
    def log(self, message):
        target = self.log_target()
        target.write(f"[LOG] {message}\n")
上一页
继承
下一页
魔术方法