Python教程38:多重继承与MRO

“大道至简,大巧若拙。”

Python支持多重继承,一个类可以继承多个父类。今天我们学习多重继承的机制、方法解析顺序(MRO)以及如何避免常见陷阱。

1. 什么是多重继承

单继承 vs 多重继承

 1# 单继承:一个父类
 2class Parent:
 3    pass
 4
 5class Child(Parent):
 6    pass
 7
 8# 多重继承:多个父类
 9class Father:
10    def skill(self):
11        return "父亲的技能"
12
13class Mother:
14    def talent(self):
15        return "母亲的天赋"
16
17class Child(Father, Mother):
18    """继承两个父类"""
19    pass
20
21# 子类拥有所有父类的方法
22c = Child()
23print(c.skill())   # 父亲的技能
24print(c.talent())  # 母亲的天赋

2. 方法解析顺序(MRO)

当多个父类有同名方法时,Python如何决定调用哪个?

基本规则

 1class A:
 2    def method(self):
 3        return "A的方法"
 4
 5class B:
 6    def method(self):
 7        return "B的方法"
 8
 9class C(A, B):
10    """继承顺序:A, B"""
11    pass
12
13c = C()
14print(c.method())  # A的方法(优先使用第一个父类)
15
16# 查看MRO
17print(C.__mro__)
18# (<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>)

MRO(Method Resolution Order)

  • 方法解析顺序
  • 决定方法查找的顺序
  • 遵循C3线性化算法

C3线性化算法

Python使用C3算法确定MRO:

 1class A:
 2    pass
 3
 4class B(A):
 5    pass
 6
 7class C(A):
 8    pass
 9
10class D(B, C):
11    """菱形继承"""
12    pass
13
14# 查看MRO
15print(D.__mro__)
16# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
17# 规则:D -> B -> C -> A -> object

MRO规则

  1. 子类在父类前
  2. 如果有多个父类,按照继承顺序
  3. 父类的父类在后

3. 菱形继承问题

什么是菱形继承

 1class A:
 2    """顶层基类"""
 3    def method(self):
 4        print("A.method")
 5
 6class B(A):
 7    """左分支"""
 8    def method(self):
 9        print("B.method")
10        super().method()
11
12class C(A):
13    """右分支"""
14    def method(self):
15        print("C.method")
16        super().method()
17
18class D(B, C):
19    """继承B和C,形成菱形"""
20    def method(self):
21        print("D.method")
22        super().method()
23
24#    A
25#   / \
26#  B   C
27#   \ /
28#    D
29
30d = D()
31d.method()
32# 输出:
33# D.method
34# B.method
35# C.method
36# A.method
37
38# MRO确保A.method只调用一次
39print(D.__mro__)
40# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)

super()的工作原理

 1class A:
 2    def __init__(self):
 3        print("A.__init__")
 4        super().__init__()
 5
 6class B(A):
 7    def __init__(self):
 8        print("B.__init__")
 9        super().__init__()
10
11class C(A):
12    def __init__(self):
13        print("C.__init__")
14        super().__init__()
15
16class D(B, C):
17    def __init__(self):
18        print("D.__init__")
19        super().__init__()
20
21d = D()
22# 输出:
23# D.__init__
24# B.__init__
25# C.__init__
26# A.__init__
27
28# super()沿着MRO链向下调用,不是调用父类!

4. Mixin模式

Mixin是多重继承的常用模式:

 1class JSONMixin:
 2    """JSON序列化Mixin"""
 3    def to_json(self):
 4        import json
 5        return json.dumps(self.__dict__)
 6
 7class XMLMixin:
 8    """XML序列化Mixin"""
 9    def to_xml(self):
10        items = ''.join(f"<{k}>{v}</{k}>" for k, v in self.__dict__.items())
11        return f"<object>{items}</object>"
12
13class Person:
14    """基类"""
15    def __init__(self, name, age):
16        self.name = name
17        self.age = age
18
19class SerializablePerson(Person, JSONMixin, XMLMixin):
20    """可序列化的Person"""
21    pass
22
23# 使用
24p = SerializablePerson("Alice", 25)
25print(p.to_json())  # {"name": "Alice", "age": 25}
26print(p.to_xml())   # <object><name>Alice</name><age>25</age></object>

Mixin命名规范

  • 类名以Mixin结尾
  • 提供特定功能
  • 不单独实例化
  • 与其他类组合使用

5. 实战示例

示例:日志和序列化Mixin

 1import json
 2import datetime
 3
 4class LogMixin:
 5    """日志Mixin"""
 6    def log(self, message):
 7        timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 8        print(f"[{timestamp}] {self.__class__.__name__}: {message}")
 9
10class SerializeMixin:
11    """序列化Mixin"""
12    def to_dict(self):
13        return {k: v for k, v in self.__dict__.items() if not k.startswith('_')}
14    
15    def to_json(self):
16        return json.dumps(self.to_dict(), default=str)
17    
18    @classmethod
19    def from_dict(cls, data):
20        return cls(**data)
21
22class User:
23    """用户基类"""
24    def __init__(self, username, email):
25        self.username = username
26        self.email = email
27    
28    def update_email(self, email):
29        self.email = email
30
31class EnhancedUser(User, LogMixin, SerializeMixin):
32    """增强的用户类"""
33    def update_email(self, email):
34        old = self.email
35        super().update_email(email)
36        self.log(f"邮箱从{old}更新为{email}")
37
38# 使用
39user = EnhancedUser("alice", "alice@old.com")
40user.log("用户创建")
41print(user.to_json())
42
43user.update_email("alice@new.com")
44print(user.to_json())

6. 多重继承最佳实践

1. 避免过深的继承层次

 1# 不好:过深的多重继承
 2class A: pass
 3class B(A): pass
 4class C(A): pass
 5class D(B, C): pass
 6class E(D): pass  # 太复杂
 7
 8# 好:简单的继承关系
 9class Base: pass
10class Mixin1: pass
11class Mixin2: pass
12class Derived(Base, Mixin1, Mixin2): pass

2. Mixin应该是独立的

 1# 好:独立的Mixin
 2class TimestampMixin:
 3    """时间戳Mixin - 不依赖其他类"""
 4    def __init__(self):
 5        self.created_at = datetime.datetime.now()
 6        super().__init__()  # 调用链中的下一个
 7
 8# 不好:依赖特定基类的Mixin
 9class BadMixin:
10    def some_method(self):
11        # 假设基类有specific_attr
12        return self.specific_attr  # 耦合度高

3. 明确继承顺序

1# 好:清晰的顺序
2class MyClass(BaseClass, Mixin1, Mixin2, Mixin3):
3    """
4    继承顺序:
5    1. BaseClass - 主要功能
6    2. Mixin1, Mixin2, Mixin3 - 附加功能
7    """
8    pass

7. 小结

今天我们学习了多重继承:

  • 多重继承:继承多个父类
  • MRO:方法解析顺序,C3算法
  • 菱形继承:super()确保每个类只调用一次
  • Mixin模式:组合功能的常用模式
  • 最佳实践:保持简单,Mixin独立

多重继承是强大但复杂的特性,需谨慎使用。


练习题

  1. 创建一个可序列化、可验证、可日志的User类(使用3个Mixin)
  2. 分析给定类的MRO,理解继承顺序
  3. 重构一个使用多重继承的代码,简化继承关系

本文代码示例

关注公众号:极客老墨

更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。

极客老墨微信公众号二维码

相关阅读