8 minutes
继承与多态
继承和多态是面向对象编程的核心特性。继承让代码复用成为可能,多态则让代码更加灵活和可扩展。本篇将深入探讨 Python 中的继承机制、多态的实现方式,以及常见的模式与最佳实践。
继承基础
继承允许一个类(子类)获取另一个类(父类)的属性和方法,从而实现代码复用。
class Animal:
def __init__(self, name: str):
self.name = name
def eat(self) -> str:
return f"{self.name} is eating"
def sleep(self) -> str:
return f"{self.name} is sleeping"
class Dog(Animal):
def bark(self) -> str:
return f"{self.name} says Woof!"
class Cat(Animal):
def meow(self) -> str:
return f"{self.name} says Meow!"
dog = Dog("Buddy")
cat = Cat("Kitty")
# 继承的方法
print(dog.eat()) # Buddy is eating
print(cat.sleep()) # Kitty is sleeping
# 子类特有的方法
print(dog.bark()) # Buddy says Woof!
print(cat.meow()) # Kitty says Meow!
方法重写(Override)
子类可以重新定义父类的方法来改变行为:
class Animal:
def __init__(self, name: str):
self.name = name
def make_sound(self) -> str:
return "Some generic sound"
class Dog(Animal):
def make_sound(self) -> str: # 重写父类方法
return f"{self.name} barks: Woof!"
class Cat(Animal):
def make_sound(self) -> str:
return f"{self.name} meows: Meow!"
animals = [Dog("Buddy"), Cat("Kitty"), Animal("Generic")]
for animal in animals:
print(animal.make_sound())
# Buddy barks: Woof!
# Kitty meows: Meow!
# Some generic sound
使用 super() 调用父类方法
super() 允许子类调用父类被重写的方法,这在扩展父类行为时非常有用:
class Vehicle:
def __init__(self, brand: str, model: str):
self.brand = brand
self.model = model
def info(self) -> str:
return f"{self.brand} {self.model}"
class Car(Vehicle):
def __init__(self, brand: str, model: str, doors: int):
super().__init__(brand, model) # 调用父类构造器
self.doors = doors
def info(self) -> str:
# 扩展父类方法
return f"{super().info()}, {self.doors} doors"
car = Car("Toyota", "Camry", 4)
print(car.info()) # Toyota Camry, 4 doors
父类初始化的常见模式
class Base:
def __init__(self, x: int):
self.x = x
class Derived(Base):
def __init__(self, x: int, y: int):
# 方式 1:使用 super()
super().__init__(x)
# 方式 2:显式调用父类(不推荐,在多重继承中会有问题)
# Base.__init__(self, x)
self.y = y
始终使用 super()。它不仅更简洁,而且在多重继承中能正确遵循 MRO。
MRO(方法解析顺序)
当一个类有多个父类时,Python 需要确定调用方法的顺序。这个顺序由 MRO(Method Resolution Order) 决定:
class A:
def method(self):
return "A"
class B(A):
def method(self):
return "B"
class C(A):
def method(self):
return "C"
class D(B, C):
pass
d = D()
print(d.method()) # B(遵循 MRO)
# 查看 MRO
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
MRO 遵循 C3 线性化算法,其核心原则是:
- 子类优先于父类
- 保持基类在定义时的顺序
- 单调性:父类的 MRO 保持不变
钻石继承问题
class A:
def __init__(self):
print("A.__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()
print(D.__mro__)
# 输出:
# D.__init__
# B.__init__
# C.__init__
# A.__init__
如果没有 super() 的协作,使用显式调用会导致 A.__init__ 被调用两次。super() 遵循 MRO,确保每个父类的构造器只调用一次。
抽象基类(ABC)
抽象基类用于定义接口和契约,强制子类实现特定的方法。Python 通过 abc 模块支持:
from abc import ABC, abstractmethod
from math import pi
class Shape(ABC):
"""形状的抽象基类"""
@abstractmethod
def area(self) -> float:
"""计算面积"""
pass
@abstractmethod
def perimeter(self) -> float:
"""计算周长"""
pass
def description(self) -> str: # 具体方法
return f"Area: {self.area():.2f}, Perimeter: {self.perimeter():.2f}"
class Circle(Shape):
def __init__(self, radius: float):
self.radius = radius
def area(self) -> float:
return pi * self.radius ** 2
def perimeter(self) -> float:
return 2 * pi * self.radius
class Rectangle(Shape):
def __init__(self, width: float, height: float):
self.width = width
self.height = height
def area(self) -> float:
return self.width * self.height
def perimeter(self) -> float:
return 2 * (self.width + self.height)
# shape = Shape() # TypeError! 不能实例化抽象类
circle = Circle(5)
rect = Rectangle(3, 4)
print(circle.description()) # Area: 78.54, Perimeter: 31.42
print(rect.description()) # Area: 12.00, Perimeter: 14.00
抽象属性
from abc import ABC, abstractmethod
class Employee(ABC):
@property
@abstractmethod
def salary(self) -> float:
pass
class FullTimeEmployee(Employee):
def __init__(self, monthly_salary: float):
self._salary = monthly_salary
@property
def salary(self) -> float:
return self._salary
class HourlyEmployee(Employee):
def __init__(self, hourly_rate: float, hours_worked: float):
self.hourly_rate = hourly_rate
self.hours_worked = hours_worked
@property
def salary(self) -> float:
return self.hourly_rate * self.hours_worked
鸭子类型与 EAFP
鸭子类型(Duck Typing)
“如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子”——Python 更关注对象的行为而非类型:
class Duck:
def quack(self):
return "Duck quacks"
class Person:
def quack(self):
return "Person imitates a duck"
def make_it_quack(thing):
# 不检查类型,直接调用方法
print(thing.quack())
make_it_quack(Duck()) # Duck quacks
make_it_quack(Person()) # Person imitates a duck
EAFP vs LBYL
# LBYL(Look Before You Leap)—— 先检查再执行
def get_value_lbyl(d, key):
if key in d:
return d[key]
return None
# EAFP(Easier to Ask for Forgiveness than Permission)—— Python 推荐
def get_value_eafp(d, key):
try:
return d[key]
except KeyError:
return None
EAFP 更符合 Python 哲学,在多线程环境中也更安全(不存在竞争窗口)。
鸭子类型 + EAFP 的威力
def process_data(data):
"""处理数据,接受多种类型的输入"""
# 尝试将数据视为列表处理
try:
for item in data:
print(f"Processing: {item}")
except TypeError:
print(f"Not iterable: {data}")
process_data([1, 2, 3]) # 列表
process_data("hello") # 字符串
process_data({1, 2, 3}) # 集合
process_data(42) # 整数 —— Not iterable
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
多重继承
class Flyer:
def fly(self) -> str:
return "Flying"
def move(self) -> str:
return "Moving through air"
class Swimmer:
def swim(self) -> str:
return "Swimming"
def move(self) -> str:
return "Moving through water"
class Duck(Flyer, Swimmer):
def move(self) -> str:
# 可以通过 super() 调用 MRO 中的特定父类
return f"{Flyer.move(self)} and {Swimmer.move(self)}"
duck = Duck()
print(duck.fly()) # Flying
print(duck.swim()) # Swimming
print(duck.move()) # Moving through air and Moving through water
多重继承的复杂性
class A:
def method(self):
return "A"
class B(A):
def method(self):
return f"B -> {super().method()}"
class C(A):
def method(self):
return f"C -> {super().method()}"
class D(B, C):
def method(self):
return f"D -> {super().method()}"
print(D().method()) # D -> B -> C -> A
print(D.__mro__)
# D -> B -> C -> A -> object
Mixin 模式
Mixin 是一种特殊的类,提供可复用的功能而不作为主要继承关系:
class PrintableMixin:
"""提供打印功能的 Mixin"""
def to_dict(self) -> dict:
return self.__dict__
def print_info(self) -> None:
for key, value in self.to_dict().items():
print(f" {key}: {value}")
class SerializableMixin:
"""提供 JSON 序列化的 Mixin"""
import json
def to_json(self) -> str:
return self.json.dumps(self.to_dict())
class TimestampMixin:
"""提供时间戳的 Mixin"""
from datetime import datetime
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.created_at = self.datetime.now()
self.updated_at = self.datetime.now()
def touch(self):
self.updated_at = self.datetime.now()
class User(TimestampMixin, PrintableMixin, SerializableMixin):
def __init__(self, name: str, email: str):
super().__init__()
self.name = name
self.email = email
user = User("Alice", "alice@example.com")
user.print_info()
# name: Alice
# email: alice@example.com
# created_at: 2026-05-16 12:00:00.123456
# updated_at: 2026-05-16 12:00:00.123456
print(user.to_json()) # {"name": "Alice", "email": "alice@example.com", ...}
Mixin 命名规范
- 类名以
Mixin或...able结尾 - Mixin 不应该被单独实例化
- Mixin 不应该有自己的
__init__(或确保调用super().__init__) - 保持 Mixin 功能单一
isinstance 检查 vs 多态
# 不好的做法:使用 isinstance 进行类型分支
def handle_payment_bad(payment):
if isinstance(payment, CreditCardPayment):
payment.process_credit_card()
elif isinstance(payment, PayPalPayment):
payment.process_paypal()
elif isinstance(payment, CryptoPayment):
payment.process_crypto()
# 每新增一种类型就要修改这里!
# 好的做法:利用多态
def handle_payment_good(payment):
payment.process()
组合优于继承
组合(Composition)指在一个类中持有另一个类的实例,相比继承更灵活:
# 继承方式 —— 可能过度设计
class Engine:
def start(self):
return "Engine started"
def stop(self):
return "Engine stopped"
class CarWithSoundSystem: # 不需要交通工具类
pass
# 组合方式 —— 更灵活
class Engine:
def start(self):
return "Engine started"
def stop(self):
return "Engine stopped"
class SoundSystem:
def play(self, song: str):
return f"Playing: {song}"
class Car:
def __init__(self, brand: str):
self.brand = brand
self.engine = Engine() # 组合
self.sound_system = SoundSystem() # 组合
def start(self):
return f"{self.brand}: {self.engine.start()}"
def play_music(self, song: str):
return self.sound_system.play(song)
# 运行时替换组件
class ElectricEngine:
def start(self):
return "Electric engine silently started"
car = Car("Tesla")
car.engine = ElectricEngine() # 运行时替换
print(car.start()) # Tesla: Electric engine silently started
推荐使用组合的场景
- 你想使用另一个类的功能,但不需要"是"它的关系
- 你需要在运行时改变行为
- 继承层次变得过于复杂
- 你有多个独立的关注点要组合
综合示例:支付系统
from abc import ABC, abstractmethod
from datetime import datetime
import json
# ---- Mixins ----
class LoggingMixin:
def log(self, message: str) -> None:
print(f"[{datetime.now():%H:%M:%S}] {message}")
class ValidatableMixin:
def validate_amount(self, amount: float) -> None:
if amount <= 0:
raise ValueError("金额必须为正数")
if amount > 1_000_000:
raise ValueError("单笔交易不能超过 1,000,000")
# ---- 抽象基类 ----
class PaymentMethod(ABC):
@abstractmethod
def process(self, amount: float) -> str:
pass
# ---- 具体实现 ----
class CreditCard(PaymentMethod, LoggingMixin, ValidatableMixin):
def __init__(self, card_number: str, holder: str):
self.card_number = card_number[-4:] # 只保留后四位
self.holder = holder
def process(self, amount: float) -> str:
self.validate_amount(amount)
self.log(f"信用卡支付: {self.holder}, ¥{amount:.2f}")
return f"信用卡 {self.card_number} 已扣款 ¥{amount:.2f}"
class PayPal(PaymentMethod, LoggingMixin, ValidatableMixin):
def __init__(self, email: str):
self.email = email
def process(self, amount: float) -> str:
self.validate_amount(amount)
self.log(f"PayPal 支付: {self.email}, ¥{amount:.2f}")
return f"PayPal 账户 {self.email} 已扣款 ¥{amount:.2f}"
class Crypto(PaymentMethod, LoggingMixin):
def __init__(self, wallet_address: str):
self.wallet_address = wallet_address
def process(self, amount: float) -> str:
fee = amount * 0.01 # 1% 手续费
self.log(f"加密货币支付: ¥{amount:.2f} + 手续费 ¥{fee:.2f}")
return f"钱包 {self.wallet_address[:8]}... 已支付 ¥{amount:.2f}"
# ---- 组合优于继承 ----
class Order:
def __init__(self, order_id: str, items: list[dict]):
self.order_id = order_id
self.items = items
self.payment: PaymentMethod | None = None
@property
def total(self) -> float:
return sum(item["price"] * item.get("qty", 1) for item in self.items)
def set_payment_method(self, payment: PaymentMethod) -> None:
self.payment = payment
def checkout(self) -> str:
if self.payment is None:
raise ValueError("未设置支付方式")
return self.payment.process(self.total)
# ---- 使用示例 ----
order = Order(
"ORD-2026-001",
[
{"name": "Python Programming Book", "price": 79.00},
{"name": "USB-C Hub", "price": 129.00, "qty": 2},
],
)
print(f"订单总计: ¥{order.total:.2f}") # 订单总计: ¥337.00
# 运行时选择支付方式
order.set_payment_method(CreditCard("1234567890123456", "Alice"))
print(order.checkout())
# 信用卡 3456 已扣款 ¥337.00
# 轻松切换支付方式
order.set_payment_method(PayPal("alice@example.com"))
print(order.checkout())
order.set_payment_method(Crypto("0xabc123def456ghi789"))
print(order.checkout())
常见陷阱
陷阱 1:忘记调用 super().__init__
class Parent:
def __init__(self):
self.value = "parent"
class Child(Parent):
def __init__(self):
# 忘记调用 super().__init__()
self.extra = "child"
c = Child()
print(c.extra) # child
# print(c.value) # AttributeError!
陷阱 2:多重继承时 super() 顺序
class Base:
def __init__(self):
print("Base")
class A(Base):
def __init__(self):
print("A")
super().__init__()
class B(Base):
def __init__(self):
print("B")
super().__init__()
class C(A, B):
def __init__(self):
print("C")
super().__init__()
print(C.__mro__)
# C -> A -> B -> Base -> object
C()
# C
# A
# B
# Base
陷阱 3:不必要的深层次继承
# 避免这样的继承链
class Animal: pass
class Mammal(Animal): pass
class Dog(Mammal): pass
class PetDog(Dog): pass
class GuideDog(PetDog): pass # 太深了!
# 使用组合代替
class GuideDog:
def __init__(self):
self.dog = PetDog()
self.training = GuideDogTraining()
小结
本篇深入探讨了 Python 的继承与多态机制:
- 单继承:子类通过继承复用父类代码,使用
super()调用父类方法 - MRO:方法解析顺序决定了多重继承下方法调用的顺序,遵循 C3 线性化算法
- 方法重写:子类重新定义父类方法来改变行为
- 抽象基类:通过
ABC和@abstractmethod定义接口契约 - 鸭子类型:“如果它走起来像鸭子”——关注行为而非类型
- EAFP vs LBYL:Python 推荐"先做,错了再处理"而非"先检查再做"
- Protocol:Python 3.8+ 的结构化子类型系统
- 多重继承与 Mixin:组合多个类的功能,Mixin 应保持单一职责
- 组合优于继承:通过持有其他类的实例来获得功能,比继承更灵活
掌握这些概念后,你将能设计出更灵活、更易维护的面向对象系统。
Summary: 继承、MRO、抽象基类、鸭子类型与多态