继承和多态是面向对象编程的核心特性。继承让代码复用成为可能,多态则让代码更加灵活和可扩展。本篇将深入探讨 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 线性化算法,其核心原则是:

  1. 子类优先于父类
  2. 保持基类在定义时的顺序
  3. 单调性:父类的 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、抽象基类、鸭子类型与多态