返回博客

26. 继承与多态:面向对象的核心机制

26. 继承与多态:面向对象的核心机制

第二十六篇:继承与多态——面向对象的核心机制

前言

**继承(Inheritance)和多态(Polymorphism)**是面向对象的两大核心特性,它们让代码更加模块化、可复用且易于扩展。本文将深入讲解 Python 中的单继承、方法重写、super() 的使用,以及多态的实现原理和鸭子类型的实际应用。

什么是继承?

基本语法

class Animal:
    """动物基类"""
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def speak(self):
        raise NotImplementedError("子类必须实现此方法")
    
    def info(self):
        return f"{self.name}, {self.age}岁"

# 通过 () 指定父类,Dog 继承自 Animal
class Dog(Animal):
    def speak(self):
        return "汪汪汪!"

# Cat 也继承自 Animal
class Cat(Animal):
    def speak(self):
        return "喵喵喵~"

# 创建实例
dog = Dog("旺财", 3)
cat = Cat("咪咪", 2)

print(dog.info())       # 旺财,3 岁
print(cat.info())       # 咪咪,2 岁
print(dog.speak())      # 汪汪汪!
print(cat.speak())      # 喵喵喵~

继承的好处:

  1. 代码复用 — info() 在 Animal 中定义一次,所有子类自动拥有
  2. 统一接口 — 所有动物都有 speak() 方法,可以统一处理
  3. 易扩展 — 新增动物只需添加新类,无需修改已有代码

super() 函数 —— 调用父类方法

基本用法

class Employee:
    """员工基类"""
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
    
    def get_details(self):
        return f"姓名:{self.name},薪资:¥{self.salary}"
    
    def work(self):
        return f"{self.name} 正在工作..."

class Developer(Employee):
    """开发人员"""
    def __init__(self, name, salary, programming_language):
        # 使用 super() 调用父类构造
        super().__init__(name, salary)
        self.programming_language = programming_language
    
    def get_details(self):
        # 在父类基础上扩展
        base_info = super().get_details()
        return f"{base_info} | 语言:{self.programming_language}"
    
    def work(self):
        return f"{self.name} 正在编写 {self.programming_language} 代码..."

dev = Developer("小明", 20000, "Python")
print(dev.get_details())  # 姓名:小明,薪资:¥20000 | 语言:Python
print(dev.work())         # 小明 正在编写 Python 代码...

super() 的执行顺序

class A:
    def __init__(self):
        print("A.__init__ 被调用")

class B(A):
    def __init__(self):
        print("B.__init__ 开始")
        super().__init__()  # 调用 A.__init__
        print("B.__init__ 结束")

class C(A):
    def __init__(self):
        print("C.__init__ 被调用")

class D(B, C):  # 多重继承
    def __init__(self):
        print("D.__init__ 开始")
        super().__init__()  # 按 MRO 顺序调用
        print("D.__init__ 结束")

d = D()

输出:

D.__init__ 开始
B.__init__ 开始
A.__init__ 被调用
B.__init__ 结束
C.__init__ 被调用
D.__init__ 结束

注意:A.__init__ 只被调用了一次(MRO 保证)。

方法重写

覆盖父类方法

class Shape:
    """图形基类"""
    def area(self):
        raise NotImplementedError("必须在子类中实现")
    
    def describe(self):
        return "一个几何图形"

class Circle(Shape):
    """圆形"""
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        import math
        return math.pi * self.radius ** 2
    
    def describe(self):
        return f"圆,半径={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 describe(self):
        return f"矩形,宽={self.width},高={self.height}"

# 多态:同一个方法名,不同行为
shapes = [Circle(5), Rectangle(4, 6)]
for shape in shapes:
    print(f"{shape.describe()},面积={shape.area():.2f}")

输出:

圆,半径=5,面积=78.54
矩形,宽=4,高=6,面积=24.00

多态与鸭子类型

多态的本质

多态 = 不同对象对同一消息做出不同响应。Python 不要求显式声明接口,而是依赖鸭子类型:

# 鸭子类型:如果它走起来像鸭子,叫起来也像鸭子,那它就是鸭子
def make_it_speak(animal):
    animal.speak()  # 只要对象有 speak() 方法就可以

class Duck:
    def speak(self):
        return "嘎嘎嘎!"

class Person:
    def speak(self):
        return "你好!"

make_it_speak(Duck())     # 嘎嘎嘎!
make_it_speak(Person())   # 你好!

isinstance() 与 issubclass()

虽然鸭子类型推崇动态性,但有时我们仍需要类型检查:

class Animal: pass
class Dog(Animal): pass
class Cat(Animal): pass

dog = Dog()

# isinstance(obj, cls) — 检查对象是否为某个类的实例
print(isinstance(dog, Dog))      # True
print(isinstance(dog, Animal))   # True — 子类的实例也是父类的实例

# issubclass(cls1, cls2) — 检查类是否继承自另一个类
print(issubclass(Dog, Animal))   # True
print(issubclass(Animal, Dog))   # False

# 内置类型检查
print(isinstance([1,2], list))   # True
print(isinstance({"a":1}, dict)) # True

ABC — 抽象基类

当我们需要强制子类实现某些方法时,使用 ABC:

from abc import ABC, abstractmethod

class Printable(ABC):
    """可打印协议"""
    @abstractmethod
    def print_content(self):
        pass

class Document(Printable):
    def __init__(self, content):
        self.content = content
    
    def print_content(self):
        print(f"[文档] {self.content}")

# 如果忘记实现 print_content,会报错
# class BrokenDoc(Printable):  # 无 print_content
#     pass

doc = Document("Hello World!")
doc.print_content()  # [文档] Hello World!

多重继承

基本用法

Python 支持一个类继承多个父类:

class Flyable:
    """飞行能力"""
    def fly(self):
        return f"{self.name} 正在飞翔..."

class Swimmable:
    """游泳能力"""
    def swim(self):
        return f"{self.name} 正在游泳..."

class Animal:
    def __init__(self, name):
        self.name = name

# 鸭子既有飞行能力又有游泳能力,还继承了 Animal
class Duck(Flyable, Swimmable, Animal):
    def __init__(self, name):
        Animal.__init__(self, name)  # 手动调用需要的父类构造函数
    
    def quack(self):
        return "嘎嘎嘎!"

duck = Duck("唐老鸭")
print(duck.quack())           # 嘎嘎嘎!
print(duck.fly())             # 唐老鸭 正在飞翔...
print(duck.swim())            # 唐老鸭 正在游泳...

MRO — 方法解析顺序

当一个类继承多个父类且有共同祖先时,Python 按照 MRO(Method Resolution Order) 查找方法:

class A:
    def method(self):
        return "A.method()"

class B(A):
    def method(self):
        return "B.method()"

class C(A):
    def method(self):
        return "C.method()"

class D(B, C):
    pass

print(D.mro())
# [<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>]

d = D()
print(d.method())  # B.method() — 优先 B,然后 C,最后 A

查看 MRO 的方法:

# 方法一:mro() 类方法
print(D.mro())

# 方法二:__mro__ 属性
print(D.__mro__)

# 方法三:help() 也会显示 MRO
help(D)

钻石继承问题

class A:
    def greet(self):
        print("A")

class B(A):
    def greet(self):
        print("B")
        super().greet()

class C(A):
    def greet(self):
        print("C")
        super().greet()

class D(B, C):
    def greet(self):
        print("D")
        super().greet()

d = D()
d.greet()
# 输出:D → B → C → A  (A 只执行一次!)

实战示例

示例一:简单的游戏角色系统

class Character:
    """角色基类"""
    def __init__(self, name, hp, attack_power):
        self.name = name
        self.max_hp = hp
        self.hp = hp
        self.attack_power = attack_power
    
    def is_alive(self):
        return self.hp > 0
    
    def take_damage(self, damage):
        self.hp = max(0, self.hp - damage)
        if not self.is_alive():
            print(f"{self.name} 被击败了!")
    
    def attack(self, target):
        print(f"{self.name} 攻击 {target.name},造成 {self.attack_power} 点伤害")
        target.take_damage(self.attack_power)


class Warrior(Character):
    """战士 — 高攻击力"""
    def special_attack(self, target):
        critical_damage = self.attack_power * 2
        print(f"{self.name} 发动猛击,对 {target.name} 造成 {critical_damage} 点暴击伤害!")
        target.take_damage(critical_damage)


class Mage(Character):
    """法师 — 范围攻击"""
    def magic_attack(self, targets):
        total_damage = self.attack_power * len(targets)
        print(f"{self.name} 释放魔法,对所有目标造成 {total_damage} 点总伤害")
        for target in targets:
            if target.is_alive():
                target.take_damage(self.attack_power)


class Healer(Mage):
    """治疗师 — 恢复队友生命"""
    def heal(self, target, amount):
        healed = min(amount, target.max_hp - target.hp)
        target.hp += healed
        print(f"{self.name} 治愈 {target.name},恢复 {healed} 点生命")


# 战斗模拟
hero = Warrior("亚瑟", 100, 25)
enemy = Mage("莫甘娜", 80, 20)
healer = Healer("露娜", 70, 15)

print("=== 回合 1 ===")
hero.attack(enemy)
print(f"{enemy.name} 剩余生命值:{enemy.hp}\n")

print("=== 回合 2 ===")
enemy.magic_attack([hero, healer])
print(f"{hero.name} 剩余生命值:{hero.hp}")
print(f"{healer.name} 剩余生命值:{healer.hp}\n")

print("=== 回合 3 ===")
healer.heal(hero, 50)
print(f"{hero.name} 恢复后生命值:{hero.hp}")

示例二:插件系统设计

from abc import ABC, abstractmethod
from typing import List

class Plugin(ABC):
    """插件基类"""
    @abstractmethod
    def execute(self, data: dict) -> dict:
        pass
    
    def name(self) -> str:
        return self.__class__.__name__


class DataCleanerPlugin(Plugin):
    def execute(self, data):
        cleaned = {}
        for key, value in data.items():
            if isinstance(value, str):
                cleaned[key] = value.strip().lower()
            else:
                cleaned[key] = value
        return cleaned


class DataValidatorPlugin(Plugin):
    def execute(self, data):
        errors = []
        if not data.get("email"):
            errors.append("缺少邮箱地址")
        if "password" in data and len(data["password"]) < 6:
            errors.append("密码长度不足")
        
        result = dict(data)
        result["errors"] = errors
        return result


class FormaterPlugin(Plugin):
    def execute(self, data):
        formatted = {}
        if "birthday" in data:
            year, month, day = map(int, data["birthday"].split("-"))
            formatted["birth_year"] = year
        else:
            formatted["birth_year"] = None
        
        formatted.update(data)
        return formatted


class PluginManager:
    """插件管理器"""
    def __init__(self):
        self.plugins: List[Plugin] = []
    
    def register(self, plugin: Plugin):
        self.plugins.append(plugin)
        print(f"注册插件:{plugin.name()}")
    
    def process(self, data):
        for plugin in self.plugins:
            data = plugin.execute(data)
        return data


# 使用
manager = PluginManager()
manager.register(DataCleanerPlugin())
manager.register(DataValidatorPlugin())
manager.register(FormaterPlugin())

user_data = {"name": "小明 ", "email": "", "password": "123"}
result = manager.process(user_data)
print(result)
# {'name': '小明', 'email': '', 'password': '123', 
#  'errors': ['缺少邮箱地址', '密码长度不足'], 
#  'birth_year': None}

最佳实践

什么时候该用继承?

# ✅ 适合用继承的场景:
# 1. "是一个"关系明确时
#    Dog IS-A Animal  →  继承

# ❌ 不适合用继承的场景:
# 1. "有一个"关系 → 考虑组合
#    Car HAS-A Engine  →  组合更好

# 正确做法:组合替代过度继承
class Engine:
    def start(self):
        return "引擎启动"

class Car:
    def __init__(self):
        self.engine = Engine()  # 组合,而非继承
    
    def start(self):
        return self.engine.start()

避免深层继承树

# ❌ 不好的设计:过深的继承链
class Animal → Mammal → Dog → GuardDog → MilitaryDog → SpecialForcesDog

# ✅ 更好的设计:使用组合 + 行为接口
class FighterBehavior:
    def fight(self):
        raise NotImplementedError()

class MilitaryDog:
    def __init__(self, fighter_behavior: FighterBehavior):
        self._fighter = fighter_behavior
    
    def fight(self):
        self._fighter.fight()

总结

本文讲解了继承与多态的核心知识:

  1. 基本继承 — 子类自动获得父类的属性和方法
  2. super() — 调用父类方法和构造函数,配合 MRO 正确使用
  3. 方法重写 — 子类覆盖父类方法实现定制行为
  4. 多态与鸭子类型 — 不同对象响应同一消息
  5. isinstance/issubclass — 类型检查工具
  6. ABC 抽象基类 — 强制实现接口契约
  7. 多重继承与 MRO — 钻石继承问题的解决方案
  8. 实战应用 — 游戏角色系统、插件系统设计
  9. 最佳实践 — 组合优于继承、避免深继承树

下一篇文章将深入讲解访问限制:私有属性与封装。


本文是 Python 教程系列的第二十六篇。