多重继承与 Mixin
本教程共 70 篇 · 第 36 篇 · 更新于 2026-07-22 · 约 5 分钟阅读
36. 多重继承与 Mixin
本节目标:理解 Python 的多重继承机制,掌握 Mixin 设计模式,能看懂 MRO 顺序并排查继承相关的问题。
之前的例子都是单继承:一个子类只有一个父类。Python 允许一个类同时继承多个父类,这叫「多重继承(multiple inheritance)」。这个功能很强大,但用不好也会带来混乱。
多重继承的基本语法
class Flyable:
def fly(self):
print("飞起来了")
class Swimmable:
def swim(self):
print("游起来了")
class Duck(Flyable, Swimmable):
pass
d = Duck()
d.fly() # 飞起来了
d.swim() # 游起来了
Duck(Flyable, Swimmable) 表示 Duck 同时继承两个类。它能使用两个父类的所有方法。
Tip多重继承不是「把多个类的功能拼在一起」这么简单。如果两个父类有同名方法,Python 要决定用哪一个。这个决定规则就是 MRO。
MRO:方法解析顺序
MRO(Method Resolution Order)定义了 Python 查找方法的顺序。你可以通过 __mro__ 属性或 mro() 方法查看:
print(Duck.__mro__)
# (<class '__main__.Duck'>, <class '__main__.Flyable'>, <class '__main__.Swimmable'>, <class 'object'>)
顺序是:自己 → 第一个父类 → 第二个父类 → … → object。Flyable 排在 Swimmable 前面,因为 class Duck(Flyable, Swimmable) 里 Flyable 写在左边。
如果两个父类有同名方法,左边的优先:
class A:
def hello(self):
print("A")
class B:
def hello(self):
print("B")
class C(A, B):
pass
C().hello() # A
NotePython 的 MRO 基于 C3 线性化算法,保证了一致性和单调性。你不需要理解算法的数学细节,只需要记住:左边的父类优先,而且不会重复访问同一个类。
菱形继承问题
33 章提过菱形继承,这里深入看一下:
Animal
/ \
Dog Cat
\ /
Hybrid
class Animal:
def __init__(self):
print("Animal init")
class Dog(Animal):
def __init__(self):
super().__init__()
print("Dog init")
class Cat(Animal):
def __init__(self):
super().__init__()
print("Cat init")
class Hybrid(Dog, Cat):
def __init__(self):
super().__init__()
print("Hybrid init")
h = Hybrid()
运行结果:
Animal init
Cat init
Dog init
Hybrid init
注意 Animal init 只执行了一次。super() 不是简单地调用父类,而是遵循 MRO 链,把同一个类的 __init__ 只走一遍。这是 C3 线性化保证的。
Mixin:只提供功能的「插件」
多重继承最常见的用途是 Mixin 模式。Mixin 类不单独使用,而是「混入」到主类里,给它增加特定功能。
一个好的 Mixin 有以下特点:
- 不定义
__init__,或者__init__只接受*args, **kwargs并传给super()。 - 不依赖主类的具体实现。
- 功能单一、可复用。
class JSONSerializableMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)
class LoggableMixin:
def log(self):
print(f"[{self.__class__.__name__}] {self}")
class User(JSONSerializableMixin, LoggableMixin):
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"User(name={self.name}, age={self.age})"
u = User('张三', 25)
print(u.to_json()) # {"name": "张三", "age": 25}
u.log() # [User] User(name=张三, age=25)
User 的主要职责是表示用户数据。JSONSerializableMixin 和 LoggableMixin 像插件一样,给它附加了序列化和日志功能。
WarningMixin 名字通常以
Mixin结尾,这是约定。看到class SomethingMixin,你就知道它不该单独实例化。
Mixin 与 super() 的配合
如果多个 Mixin 和主类都涉及初始化,要用 super() 配合 *args, **kwargs,让 MRO 链顺畅传递:
class BaseModel:
def __init__(self, name, **kwargs):
self.name = name
super().__init__(**kwargs)
class TimestampMixin:
def __init__(self, created_at=None, **kwargs):
from datetime import datetime
self.created_at = created_at or datetime.now()
super().__init__(**kwargs)
class VersionMixin:
def __init__(self, version=1, **kwargs):
self.version = version
super().__init__(**kwargs)
class Document(BaseModel, TimestampMixin, VersionMixin):
pass
d = Document(name='报告', version=2)
print(d.name) # 报告
print(d.version) # 2
print(d.created_at) # 当前时间
每个类的 __init__ 都接收 **kwargs,提取自己需要的参数,剩下的传给 super()。这样无论继承顺序怎么变,初始化都能正常工作。
什么时候用继承,什么时候用组合
继承是「is-a」关系:狗是一种动物。组合是「has-a」关系:汽车有一个引擎。
如果功能之间没有明确的层次关系,组合往往比重继承更清晰:
# 组合写法
class Engine:
def start(self):
print("引擎启动")
class Car:
def __init__(self):
self.engine = Engine()
def start(self):
self.engine.start()
print("汽车行驶")
多重继承和 Mixin 适合「横切关注点」:日志、序列化、权限检查等功能,很多类都需要,但又不属于类的核心职责。用 Mixin 混入,比在每个类里重复写代码干净得多。
小结
- 多重继承用
class Child(A, B)语法,MRO 决定方法查找顺序。 __mro__和mro()可以查看解析顺序。- Mixin 是提供单一功能的「插件类」,名字以
Mixin结尾。 - 多继承的初始化配合
super()和**kwargs最安全。 - 核心逻辑用组合,横切功能用 Mixin,别为了继承而继承。
多重继承就像厨房里的多功能料理机。单继承是榨汁机,专一但局限;多重继承能把榨汁、打蛋、绞肉拼在一起,但零件多了也容易卡壳。用之前想清楚:这个功能真的属于「是一种」关系吗?
来源:参考了 runoob「Python3 面向对象」、liaoxuefeng「多重继承」、pythondoc「9. 类」等,改写后所得。