第 12 章 继承与多态
12.1 单继承与方法重写
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} makes a sound"
def __repr__(self):
return f"{type(self).__name__}({self.name!r})"
class Dog(Animal):
def speak(self): # 重写(override)父类方法
return f"{self.name} says: Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says: Meow!"
def purr(self): # 子类独有的方法
return f"{self.name} purrs..."
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # Buddy says: Woof!
print(cat.speak()) # Whiskers says: Meow!
print(cat.purr()) # Whiskers purrs...
# isinstance 和 issubclass
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True — Dog 是 Animal 的子类
print(issubclass(Dog, Animal)) # True
12.2 super() 的正确用法
super() 用于调用父类(或 MRO 中的下一个类)的方法:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # 调用父类的 __init__
self.breed = breed
dog = Dog("Buddy", 3, "Labrador")
print(dog.name, dog.age, dog.breed)
super() 在多继承中的行为
super() 不是”调用父类”,而是”调用 MRO 中的下一个类”:
class A:
def method(self):
print("A.method")
class B(A):
def method(self):
print("B.method")
super().method() # 调用 MRO 中下一个,不一定是 A
class C(A):
def method(self):
print("C.method")
super().method()
class D(B, C):
def method(self):
print("D.method")
super().method()
D().method()
# D.method
# B.method
# C.method ← B 的 super() 调用的是 C,不是 A!
# A.method
print(D.__mro__)
# (D, B, C, A, object)
协作式多继承
正确使用 super() 实现协作式多继承:
class Base:
def __init__(self, **kwargs):
# 吞掉所有剩余参数,终止 super() 链
pass
class Name(Base):
def __init__(self, name, **kwargs):
super().__init__(**kwargs)
self.name = name
class Age(Base):
def __init__(self, age, **kwargs):
super().__init__(**kwargs)
self.age = age
class Person(Name, Age):
pass
p = Person(name="Alice", age=30)
print(p.name, p.age) # Alice 30
12.3 多继承与 MRO(C3 线性化)
Python 支持多继承,方法查找顺序由 MRO(Method Resolution Order) 决定:
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
# MRO: D → B → C → A → object
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
# 也可以用 mro() 方法
print(D.mro())
C3 线性化算法
MRO 使用 C3 线性化算法,保证:
- 子类在父类之前
- 多个父类的相对顺序保持不变
- 如果无法满足以上规则,抛出 TypeError
# 菱形继承问题
# A
# / \
# B C
# \ /
# D
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
pass
D().method() # B — 按 MRO 顺序,B 在 C 前面
# 不合法的继承结构会报错
# class X(A, B): pass # 如果 MRO 无法线性化,会 TypeError
12.4 Mixin 设计模式
Mixin 是一种不独立使用、专门用来”混入”其他类的小型类:
class JsonMixin:
"""提供 JSON 序列化能力"""
def to_json(self):
import json
return json.dumps(self.__dict__, ensure_ascii=False)
@classmethod
def from_json(cls, json_str):
import json
data = json.loads(json_str)
return cls(**data)
class ReprMixin:
"""提供友好的字符串表示"""
def __repr__(self):
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{type(self).__name__}({attrs})"
class ValidateMixin:
"""提供数据验证能力"""
def validate(self):
for attr, rules in getattr(self, '_validators', {}).items():
value = getattr(self, attr)
for rule in rules:
rule(attr, value)
# 通过多继承混入多种能力
class User(JsonMixin, ReprMixin):
def __init__(self, name, age):
self.name = name
self.age = age
user = User("Alice", 30)
print(user) # User(name='Alice', age=30)
print(user.to_json()) # {"name": "Alice", "age": 30}
user2 = User.from_json('{"name": "Bob", "age": 25}')
print(user2) # User(name='Bob', age=25)
Mixin 的原则:
- Mixin 不应该有
__init__(或尽量简单) - Mixin 应该只提供一组相关的方法
- 命名以
Mixin结尾,表明意图 - 放在继承列表的前面(MRO 优先)
12.5 抽象基类 ABC 与 @abstractmethod
抽象基类定义接口,强制子类实现特定方法:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float:
"""计算面积"""
...
@abstractmethod
def perimeter(self) -> float:
"""计算周长"""
...
def description(self):
"""非抽象方法,子类可选重写"""
return f"{type(self).__name__}: area={self.area():.2f}"
# 不能实例化抽象类
# Shape() # TypeError: Can't instantiate abstract class Shape
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
# 如果忘了实现抽象方法,实例化时会报错
# class Incomplete(Shape):
# def area(self):
# return 0
# Incomplete() # TypeError: Can't instantiate... perimeter
# 多态使用
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.description())
# Circle: area=78.54
# Rectangle: area=12.00
抽象属性
from abc import ABC, abstractmethod
class Vehicle(ABC):
@property
@abstractmethod
def max_speed(self) -> float:
...
@abstractmethod
def fuel_type(self) -> str:
...
class Car(Vehicle):
@property
def max_speed(self):
return 200.0
def fuel_type(self):
return "gasoline"
12.6 鸭子类型与协议(Protocol)
“如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。”
Python 不关心对象的类型,只关心它有没有需要的方法:
class Duck:
def quack(self):
return "Quack!"
def walk(self):
return "Walking like a duck"
class Person:
def quack(self):
return "I'm quacking like a duck!"
def walk(self):
return "I'm walking like a duck!"
class Robot:
def quack(self):
return "Beep boop quack"
def walk(self):
return "Mechanical walking"
def duck_test(thing):
# 不检查类型,只要有 quack 和 walk 方法就行
print(thing.quack())
print(thing.walk())
# 所有对象都能通过"鸭子测试"
duck_test(Duck())
duck_test(Person())
duck_test(Robot())
协议(Protocol)— 结构化子类型(Python 3.8+)
Protocol 是鸭子类型的形式化——定义结构化接口,无需继承:
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> str:
...
class Circle:
def draw(self) -> str:
return "Drawing a circle"
class Square:
def draw(self) -> str:
return "Drawing a square"
def render(shape: Drawable) -> None:
print(shape.draw())
# Circle 和 Square 没有继承 Drawable,但符合其"结构"
render(Circle()) # Drawing a circle — 类型检查通过
render(Square()) # Drawing a square — 类型检查通过
ABC vs Protocol
| ABC | Protocol | |
|---|---|---|
| 需要继承 | 是 | 否 |
| 运行时检查 | isinstance() 可用 | 默认不可用(需 runtime_checkable) |
| 适用场景 | 明确的类层级 | 鸭子类型、第三方类 |
| 哲学 | 名义类型(Nominal) | 结构类型(Structural) |
from typing import Protocol, runtime_checkable
@runtime_checkable
class Sized(Protocol):
def __len__(self) -> int:
...
# 支持 isinstance 检查
print(isinstance([1, 2, 3], Sized)) # True — list 有 __len__
print(isinstance("hello", Sized)) # True — str 有 __len__
print(isinstance(42, Sized)) # False — int 没有 __len__
本章小结:Python 的继承体系以 MRO 和 C3 线性化为基础,通过
super()实现协作式多继承。Mixin 是多继承的最佳实践,ABC 提供了形式化的接口约束。但 Python 的核心哲学是鸭子类型——关注行为而非身份,Protocol 将这一哲学与类型系统优雅结合。