2026-03-10
Python
00

目录

多继承与 MRO
super() 的真正含义
Mixin 模式
抽象基类
isinstance 和继承链

Python 的继承就是让一个类直接拿到另一个类的属性和方法,不用重复写一遍。子类继承父类之后,父类有的东西子类全都能用,想改就重写对应的方法就行。

Python 跟 Java 不一样的地方在于它支持 多继承,一个子类可以同时继承多个父类。多继承虽然灵活,但也带来了方法冲突的问题,Python 用 MRO 来确定方法调用顺序。来看个最直观的例子:

Python
class Animal: def __init__(self, name): self.name = name def speak(self): raise NotImplementedError class Dog(Animal): def speak(self): return f"{self.name}: Woof!" class Cat(Animal): def speak(self): return f"{self.name}: Meow!" dog = Dog("Buddy") print(dog.speak()) # Buddy: Woof! print(isinstance(dog, Animal)) # True

子类 Dog 和 Cat 继承了 Animal 的 init,不用重复写初始化逻辑,只需要重写 speak 方法就能实现各自的行为。isinstance 也能正确识别继承关系。

Animal 作为基类,定义了 name 属性和 speak 方法。Dog 继承 Animal,重写 speak 返回 "Woof!"。Cat 继承 Animal,重写 speak 返回 "Meow!"。

isinstance(dog, Animal) 返回 True,说明子类实例也是父类的实例。

image.png

多继承与 MRO

单继承很好理解,方法查找就是沿着继承链一路往上找。但 Python 支持多继承,问题就来了:两个父类有同名方法,到底调谁的?

经典的 菱形继承 问题长这样:

Python
class A: def do(self): print("A") class B(A): def do(self): print("B") class C(A): def do(self): print("C") class D(B, C): pass d = D() d.do() # 输出 B print(D.__mro__) # (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)

D 同时继承了 B 和 C,B 和 C 又都继承了 A,形成一个菱形。Python 用 C3 线性化算法来决定方法解析顺序,核心规则就两条:

1)子类永远排在父类前面 2)如果有多个父类,按照继承列表里的声明顺序来

所以 D 的 MRO 是 D → B → C → A → object,调用 d.do() 找到 B 就停了。

可以通过 ClassName.mro 或 ClassName.mro() 查看任何类的方法解析顺序。如果 C3 算法算不出合法的线性化顺序,Python 会直接抛 TypeError,连类都定义不了。

image.png

super() 的真正含义

很多人以为 super() 就是"调用父类的方法",在单继承下确实可以这么理解。但在多继承体系里,super() 调用的其实是 MRO 里的下一个类,不一定是直接父类。

Python
class A: def __init__(self): print("A init") super().__init__() class B(A): def __init__(self): print("B init") super().__init__() class C(A): def __init__(self): print("C init") super().__init__() class D(B, C): def __init__(self): print("D init") super().__init__() D() # D init → B init → C init → A init

注意 B 的 super().init() 调用的不是 A,是 C,因为 D 的 MRO 是 D → B → C → A。这就是为什么在多继承场景下,每个类的 init 都要调用 super().init(),否则调用链会断掉。

Mixin 模式

实际项目中很少直接搞复杂的多继承,更常见的做法是用 Mixin。Mixin 类本身不独立使用,只是把一组功能"混入"到目标类里。Django 的 class-based views 就是典型代表:

Python
class LogMixin: def log(self, message): print(f"[{self.__class__.__name__}] {message}") class SerializeMixin: def to_dict(self): return self.__dict__ class User(LogMixin, SerializeMixin): def __init__(self, name, age): self.name = name self.age = age u = User("Alice", 30) u.log("created") # [User] created print(u.to_dict()) # {'name': 'Alice', 'age': 30}

Mixin 的命名约定是以 Mixin 结尾,一看就知道不是普通的父类。Django 的 LoginRequiredMixin、PermissionRequiredMixin 都是这个套路。

抽象基类

Python 的 abc 模块提供了抽象基类的支持,能强制子类实现指定方法。跟 Java 的 abstract class 思路一样,没实现抽象方法就不让实例化:

Python
from abc import ABC, abstractmethod class PaymentGateway(ABC): @abstractmethod def charge(self, amount): pass @abstractmethod def refund(self, transaction_id): pass class StripeGateway(PaymentGateway): def charge(self, amount): return f"Stripe charged {amount}" def refund(self, transaction_id): return f"Stripe refunded {transaction_id}" # PaymentGateway() # TypeError: 抽象类不能实例化 gateway = StripeGateway() print(gateway.charge(100)) # Stripe charged 100

Python 3.3 之后,collections.abc 模块把常用的抽象基类都内置了,像 Iterable、Mapping、Sequence 这些。自定义容器类的时候继承它们,能确保接口的完整性。

isinstance 和继承链

isinstance 检查的是整条继承链,不只是直接父类。issubclass 同理:

Python
print(isinstance(gateway, StripeGateway)) # True print(isinstance(gateway, PaymentGateway)) # True print(isinstance(gateway, ABC)) # True print(issubclass(StripeGateway, PaymentGateway)) # True

Python 还支持通过 subclasshook 实现虚拟子类,不需要真的继承也能让 isinstance 返回 True,collections.abc 里的 Hashable 就是这么干的。

本文作者:Dewar

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!