首页 / Python3 入门教程 / 多重继承与 Mixin

Python3 入门教程

多重继承与 Mixin

本教程共 70 篇 · 第 36 篇 · 更新于 2026-07-22 · 约 5 分钟阅读

PythonPython3 入门教程多重继承MixinMROsuper()

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'>)

顺序是:自己 → 第一个父类 → 第二个父类 → … → objectFlyable 排在 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
Note

Python 的 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 的主要职责是表示用户数据。JSONSerializableMixinLoggableMixin 像插件一样,给它附加了序列化和日志功能。

Warning

Mixin 名字通常以 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. 类」等,改写后所得。