
📖 开篇导读
在前几节课中,我们学习了面向对象的两大特性:封装(第23课)和继承(第24课)。今天我们将迎来面向对象三大特性的最后一位——多态。
多态(Polymorphism)源自希腊语,意为“多种形态”。在编程中,多态指的是同一个方法调用,由于对象不同,可能表现出不同的行为。它让代码更具灵活性和扩展性,是许多设计模式的基础。
💡 工作场景:图形系统中,
draw()方法在Circle、Rectangle、Triangle中有不同实现;支付系统中,pay()方法在Alipay、WechatPay中各有逻辑;游戏开发中,attack()方法在Warrior、Mage、Archer中有不同效果。多态让你写出通用代码,无需关心具体类型。
Python的多态与其他语言略有不同:它是“鸭子类型”的——只要对象有相应的方法,就可以调用,不要求继承关系。此外,Python提供了**抽象基类(ABC)**来定义接口,强制子类实现特定方法。
本课将学习:
- 方法重写的原理和应用
- 多态的概念及Python中的实现方式
- 抽象基类(ABC)与抽象方法
- 接口的模拟与
@abstractmethod - 实战:图形系统、支付系统、插件架构
学完本课,你将能够设计出高内聚、低耦合的可扩展系统。
🎯 学习目标
| 目标编号 | 具体掌握内容 | 对应面试/工作价值 |
|---|---|---|
| 1️⃣ | 理解方法重写的实质与super()的协作机制 | 扩展父类功能 |
| 2️⃣ | 掌握多态的概念,区分重写与重载 | 面试高频“多态与重载区别” |
| 3️⃣ | 深入理解鸭子类型及Python的多态实现方式 | 写出更Pythonic的代码 |
| 4️⃣ | 熟练使用abc模块定义抽象基类和抽象方法 | 设计接口,约束子类 |
| 5️⃣ | 理解抽象类与接口在Python中的统一实现 | 大型项目架构 |
| 6️⃣ | 实战:实现一个基于抽象基类的插件系统 | 掌握可扩展设计 |
🔥 面试考点:“什么是多态?Python如何实现多态?”“抽象类和接口的区别?”“鸭子类型是什么?”“什么时候使用抽象基类?”
📚 知识点理论精讲
一、方法重写(Override)再探
1.1 重写的本质
子类重新定义父类中已有的方法,称为方法重写。在Python中,所有方法都是虚方法(可被重写)。
class Parent:
def method(self):
print("Parent")
class Child(Parent):
def method(self):
print("Child") # 重写
c = Child()
c.method() # Child
1.2 扩展而非覆盖
通常重写时,我们希望在子类逻辑之外,仍然执行父类的部分逻辑。使用super()可以达到扩展效果。
class Animal:
def speak(self):
print("动物发出声音")
class Dog(Animal):
def speak(self):
super().speak() # 先调用父类
print("汪汪") # 再添加子类行为
d = Dog()
d.speak()
1.3 重写与重载的区别
- 重写(Override):子类重新定义父类的方法,方法名、参数列表相同。
- 重载(Overload):同一个类中定义多个同名但参数列表不同的方法。Python不支持传统的方法重载,但可以通过默认参数、可变参数或使用
functools.singledispatch实现类似效果。
# Python中的“重载”模拟
class Math:
def add(self, a, b, c=None):
if c is None:
return a + b
return a + b + c
二、多态:同一接口,多种形态
2.1 多态的基本概念
多态允许不同类型的对象对同一消息作出不同响应。在面向对象编程中,多态通常通过继承和虚方法实现。
class Shape:
def area(self):
pass
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return 3.14 * self.r ** 2
class Rectangle(Shape):
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def print_area(shape):
print(f"面积: {shape.area()}")
c = Circle(5)
r = Rectangle(4, 6)
print_area(c) # 面积: 78.5
print_area(r) # 面积: 24
2.2 鸭子类型(Duck Typing)
Python的多态不要求显式的继承关系。只要一个对象有符合要求的方法,就可以使用。这就是“鸭子类型”:如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。
class Duck:
def quack(self):
print("嘎嘎")
class Person:
def quack(self):
print("模仿鸭子嘎嘎")
def make_quack(obj):
obj.quack()
make_quack(Duck()) # 嘎嘎
make_quack(Person()) # 模仿鸭子嘎嘎
💡 工作应用:鸭子类型让代码更灵活,你不需要为了复用而设计复杂的继承体系。只要对象实现了所需的方法,就可以传入。
2.3 多态的优势
- 开闭原则:对扩展开放,对修改关闭。新增类型不需要修改原有函数。
- 代码复用:通用逻辑写在高层,具体实现写在底层。
- 可维护性:接口统一,降低耦合。
三、抽象基类(Abstract Base Class, ABC)
抽象基类不能实例化,用于定义一组必须被实现的接口(抽象方法)。Python通过abc模块提供支持。
3.1 定义抽象基类
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("汪汪")
# a = Animal() # TypeError: Can't instantiate abstract class Animal
d = Dog()
d.speak()
3.2 抽象方法可以有实现
抽象方法也可以有默认实现,子类可以不重写,但如果重写,需要调用super()来使用默认逻辑。
class Vehicle(ABC):
@abstractmethod
def start(self):
print("车辆启动中...")
@abstractmethod
def stop(self):
pass
class Car(Vehicle):
def start(self):
super().start()
print("汽车引擎点火")
def stop(self):
print("汽车熄火")
3.3 @abstractmethod与@property结合
可以定义抽象属性。
from abc import ABC, abstractmethod
class Person(ABC):
@property
@abstractmethod
def name(self):
pass
class Student(Person):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
3.4 抽象基类与register注册虚拟子类
可以使用register方法将一个类注册为抽象基类的“虚拟子类”,即使它没有显式继承。isinstance和issubclass会返回True,但虚拟子类不需要实现抽象方法。
class Duck:
def quack(self):
print("嘎嘎")
Animal.register(Duck) # 将Duck注册为Animal的虚拟子类
print(issubclass(Duck, Animal)) # True
duck = Duck()
print(isinstance(duck, Animal)) # True
四、接口的模拟
在其他语言(如Java)中,接口(Interface)是一种纯抽象类,只能包含方法签名,不能有实现。Python没有专门的接口关键字,但可以通过抽象基类模拟。
4.1 定义“接口”(纯抽象类)
from abc import ABC, abstractmethod
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def get_size(self):
pass
class Circle(Drawable):
def draw(self):
print("画圆")
def get_size(self):
return (10, 10)
4.2 多重“实现”接口
一个类可以继承多个抽象基类(接口)。
class Serializable(ABC):
@abstractmethod
def to_dict(self):
pass
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
class Shape(Circle, Serializable):
pass # 需要实现所有抽象方法
五、多态与抽象基类的实战模式
5.1 策略模式(简单版)
from abc import ABC, abstractmethod
class SortStrategy(ABC):
@abstractmethod
def sort(self, data):
pass
class BubbleSort(SortStrategy):
def sort(self, data):
print("使用冒泡排序")
return sorted(data)
class QuickSort(SortStrategy):
def sort(self, data):
print("使用快速排序")
return sorted(data) # 模拟
class Context:
def __init__(self, strategy: SortStrategy):
self.strategy = strategy
def execute_sort(self, data):
return self.strategy.sort(data)
data = [3,1,4,2]
ctx = Context(BubbleSort())
ctx.execute_sort(data)
5.2 插件系统架构
import importlib
class Plugin(ABC):
@abstractmethod
def run(self, context):
pass
class PluginManager:
def __init__(self):
self.plugins = []
def register(self, plugin: Plugin):
self.plugins.append(plugin)
def run_all(self, context):
for plugin in self.plugins:
plugin.run(context)
💻 代码案例实操
案例1:方法重写与扩展
"""
override_demo.py
演示方法重写的各种模式
"""
class Logger:
def log(self, message, level="INFO"):
print(f"[{level}] {message}")
class TimestampLogger(Logger):
from datetime import datetime
def log(self, message, level="INFO"):
timestamp = self.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
super().log(f"{timestamp} - {message}", level)
class ErrorLogger(TimestampLogger):
def log(self, message, level="INFO"):
# 如果是错误级别,强制转换为ERROR
if "error" in message.lower():
level = "ERROR"
super().log(message, level)
# 使用
logger = ErrorLogger()
logger.log("系统启动")
logger.log("出现error", "WARNING") # 级别会被强制转为ERROR
案例2:多态与鸭子类型
"""
polymorphism_demo.py
演示多态和鸭子类型
"""
# 使用继承的多态
class Bird:
def fly(self):
print("鸟飞")
class Airplane:
def fly(self):
print("飞机飞")
class Superman:
def fly(self):
print("超人飞")
def make_it_fly(obj):
# 鸭子类型:不关心是什么,只关心有没有fly方法
obj.fly()
bird = Bird()
plane = Airplane()
superman = Superman()
make_it_fly(bird)
make_it_fly(plane)
make_it_fly(superman)
# 另一个例子:不同的对象,相同的接口
class Cat:
def sound(self):
return "喵"
class Dog:
def sound(self):
return "汪"
class Cow:
def sound(self):
return "哞"
animals = [Cat(), Dog(), Cow()]
for animal in animals:
print(animal.sound())
案例3:抽象基类强制实现方法
"""
abc_force.py
使用抽象基类强制子类实现必要方法
"""
from abc import ABC, abstractmethod
class DataProcessor(ABC):
@abstractmethod
def load_data(self):
pass
@abstractmethod
def process(self):
pass
@abstractmethod
def save_result(self):
pass
def run(self):
self.load_data()
self.process()
self.save_result()
print("处理完成")
class CsvProcessor(DataProcessor):
def load_data(self):
print("加载CSV文件")
def process(self):
print("处理CSV数据")
def save_result(self):
print("保存结果为CSV")
class JsonProcessor(DataProcessor):
def load_data(self):
print("加载JSON文件")
def process(self):
print("处理JSON数据")
def save_result(self):
print("保存结果为JSON")
# processor = DataProcessor() # TypeError
csv = CsvProcessor()
csv.run()
json = JsonProcessor()
json.run()
案例4:抽象属性与抽象类方法
"""
abc_property.py
演示抽象属性和抽象类方法
"""
from abc import ABC, abstractmethod
class Config(ABC):
@property
@abstractmethod
def host(self):
pass
@property
@abstractmethod
def port(self):
pass
@classmethod
@abstractmethod
def default(cls):
pass
class DevelopmentConfig(Config):
@property
def host(self):
return "localhost"
@property
def port(self):
return 8080
@classmethod
def default(cls):
return cls()
class ProductionConfig(Config):
@property
def host(self):
return "prod.example.com"
@property
def port(self):
return 80
@classmethod
def default(cls):
return cls()
def run_server(config: Config):
print(f"启动服务器 {config.host}:{config.port}")
run_server(DevelopmentConfig.default())
run_server(ProductionConfig.default())
案例5:虚拟子类注册
"""
virtual_subclass.py
演示注册虚拟子类,让已有类“属于”抽象基类
"""
from abc import ABC, abstractmethod
class Iterable(ABC):
@abstractmethod
def __iter__(self):
pass
# 注册list为Iterable的虚拟子类
Iterable.register(list)
print(issubclass(list, Iterable)) # True
print(isinstance([1,2,3], Iterable)) # True
# 自定义一个类,不继承Iterable,但注册后也会被认为是子类
class MyCollection:
def __iter__(self):
return iter([1,2,3])
Iterable.register(MyCollection)
mc = MyCollection()
print(isinstance(mc, Iterable)) # True
# 但实际调用时,如果MyCollection没有实现 __iter__ 会怎样?
# 注册只影响isinstance/issubclass,不影响实际调用
案例6:支付系统(多态+抽象基类)
"""
payment_system.py
模拟支付系统,演示抽象基类和多态
"""
from abc import ABC, abstractmethod
import random
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
pass
@abstractmethod
def refund(self, amount):
pass
class Alipay(PaymentMethod):
def pay(self, amount):
print(f"支付宝支付 {amount} 元")
# 模拟支付请求
return True
def refund(self, amount):
print(f"支付宝退款 {amount} 元")
return True
class WechatPay(PaymentMethod):
def pay(self, amount):
print(f"微信支付 {amount} 元")
return True
def refund(self, amount):
print(f"微信退款 {amount} 元")
return True
class CreditCard(PaymentMethod):
def __init__(self, card_number):
self.card_number = card_number
def pay(self, amount):
if random.random() > 0.95:
print("信用卡支付失败(模拟网络错误)")
return False
print(f"信用卡 {self.card_number[-4:]} 支付 {amount} 元")
return True
def refund(self, amount):
print(f"信用卡 {self.card_number[-4:]} 退款 {amount} 元")
return True
class Order:
def __init__(self, order_id, amount):
self.order_id = order_id
self.amount = amount
self.paid = False
def process_payment(self, payment_method: PaymentMethod):
if payment_method.pay(self.amount):
self.paid = True
print(f"订单 {self.order_id} 支付成功")
else:
print(f"订单 {self.order_id} 支付失败")
# 使用
order1 = Order("1001", 299)
order2 = Order("1002", 599)
alipay = Alipay()
wechat = WechatPay()
credit = CreditCard("1234-5678-9012-3456")
order1.process_payment(alipay)
order2.process_payment(credit)
案例7:图形编辑器(综合多态)
"""
graphic_editor.py
图形编辑器,使用多态实现绘制、移动、缩放
"""
from abc import ABC, abstractmethod
import math
class Shape(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def move(self, dx, dy):
pass
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
def draw(self):
print(f"画圆 圆心=({self.x},{self.y}) 半径={self.radius}")
def move(self, dx, dy):
self.x += dx
self.y += dy
print(f"圆形移动到 ({self.x},{self.y})")
def area(self):
return math.pi * self.radius ** 2
class Rectangle(Shape):
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
def draw(self):
print(f"画矩形 左上角=({self.x},{self.y}) 宽={self.width} 高={self.height}")
def move(self, dx, dy):
self.x += dx
self.y += dy
print(f"矩形移动到 ({self.x},{self.y})")
def area(self):
return self.width * self.height
class Triangle(Shape):
def __init__(self, x1, y1, x2, y2, x3, y3):
self.points = [(x1, y1), (x2, y2), (x3, y3)]
def draw(self):
print(f"画三角形 顶点={self.points}")
def move(self, dx, dy):
self.points = [(x+dx, y+dy) for x, y in self.points]
print(f"三角形移动到新顶点={self.points}")
def area(self):
# 使用鞋带公式计算多边形面积
x = [p[0] for p in self.points]
y = [p[1] for p in self.points]
return 0.5 * abs(sum(x[i]*y[(i+1)%3] - x[(i+1)%3]*y[i] for i in range(3)))
class Editor:
def __init__(self):
self.shapes = []
def add_shape(self, shape: Shape):
self.shapes.append(shape)
def draw_all(self):
for shape in self.shapes:
shape.draw()
def move_all(self, dx, dy):
for shape in self.shapes:
shape.move(dx, dy)
def total_area(self):
return sum(shape.area() for shape in self.shapes)
# 使用
editor = Editor()
editor.add_shape(Circle(10, 10, 5))
editor.add_shape(Rectangle(20, 20, 30, 40))
editor.add_shape(Triangle(0,0, 10,0, 5,10))
editor.draw_all()
print(f"总面积: {editor.total_area():.2f}")
editor.move_all(5, 5)
editor.draw_all()
⚠️ 易错点避坑总结
| 序号 | 坑点描述 | 后果 | 解决方案 |
|---|---|---|---|
| 1 | 重写方法时忘记调用super(),导致父类初始化逻辑未执行 | 父类属性未设置,可能AttributeError | 必要时在子类__init__等中调用super() |
| 2 | 在抽象基类中定义了抽象方法,但子类未实现 | 实例化子类时TypeError | 确保所有抽象方法都被实现 |
| 3 | 以为抽象基类可以像接口一样多重继承,但多个抽象基类可能有同名抽象方法 | 子类需要同时实现,可能导致冲突 | 合理设计方法名,或使用组合 |
| 4 | 滥用鸭子类型,没有文档或类型提示,导致调用方传入错误类型的对象 | 运行时AttributeError | 可以使用类型注解(typing.Protocol)或抽象基类进行类型检查 |
| 5 | 在抽象方法中编写了实现,但子类忘记调用super() | 父类的默认实现未执行 | 在文档中说明是否需要调用super() |
| 6 | 使用isinstance检查具体类型,而不是接口 | 违反开闭原则,添加新类型需要修改代码 | 使用isinstance(obj, AbstractClass)或鸭子类型 |
| 7 | 抽象基类中混合了具体方法和抽象方法,子类容易遗漏抽象方法 | 子类仍可实例化?不,只要有抽象方法就不可实例化 | 定期检查抽象基类,或使用工具 |
| 8 | 注册虚拟子类后,以为自动获得了抽象方法实现 | 运行时调用未实现的方法会出错 | 注册只是类型检查的标记,不提供实现 |
| 9 | 多继承抽象基类时,MRO导致方法解析顺序异常 | 调用父类方法不如预期 | 查看__mro__,使用super() |
| 10 | 在抽象属性setter中只定义了getter | 子类如果没有实现setter,但需要设置值时会出错 | 抽象属性通常只定义getter,或者同时定义setter为抽象 |
📝 课后实战练习题
第1题:方法重写练习
定义一个Vehicle基类,有start()和stop()方法,打印通用信息。定义Car、Bike子类,重写start()和stop(),分别输出“汽车启动/熄火”、“自行车骑行/刹车”。在子类中调用父类方法后添加特有逻辑。创建实例测试。
第2题:多态与函数
编写一个函数describe(obj),接收任意对象,如果对象有description方法就调用它,否则返回该对象的字符串表示。演示多态行为。
第3题:抽象基类实现数据库驱动
定义抽象基类DatabaseDriver,包含connect()、query(sql)、close()抽象方法。实现两个子类:MySQLDriver和SQLiteDriver,分别模拟(print代替实际连接)。编写一个函数execute_query(driver, sql),接收驱动对象并执行查询。
第4题:虚拟子类练习
定义抽象基类Iterable,带__iter__抽象方法。然后定义MyCollection类,实现了__iter__但不继承Iterable。使用register将其注册为虚拟子类。验证isinstance和issubclass。然后尝试迭代。
第5题:图形系统扩展
在案例7的图形编辑器中,新增Square类,继承自Rectangle或Shape,实现相应方法。并在Editor中添加group_shapes功能(组合模式):创建一个Group类,也继承Shape,包含多个Shape,其move和draw方法委托给内部形状。测试组合。
第6题:支付系统添加新方式
在案例6的支付系统中,添加PayPal支付方式,包含email属性。实现pay和refund方法,模拟调用API。修改Order类,使其支持接收多种支付方式,并统计支付总金额。
第7题:插件系统
设计一个简单的插件系统:
- 定义
Plugin抽象基类,包含execute()抽象方法。 - 创建两个插件:
HelloPlugin和TimePlugin。 - 实现
PluginManager,能够动态加载插件(可以手动添加,或扫描某个目录)。 - 实现一个命令行界面,让用户选择执行哪个插件。
🔜 下节课预告
面向对象的三大特性已全部讲完:封装、继承、多态。接下来我们将深入类的内部:类属性、实例属性、类方法、静态方法的应用场景,以及一些高级技巧。
第26课:类属性、实例属性、类方法、静态方法应用场景全解
内容包括:
- 类属性与实例属性的深入对比与最佳实践
- 类方法作为工厂方法
- 静态方法作为工具函数组织
property的高级用法- 实战:计数器、配置管理器
学完本课,你将能熟练选用合适的属性和方法类型,写出更清晰的面向对象代码。
🌟 学习鼓励:多态是面向对象的高级特性,它让代码能够优雅地应对变化。理解鸭子类型,你会发现自己不再需要写大量的
isinstance判断。抽象基类是大型项目的“契约”,掌握它你就能设计出稳定的API。多做练习,把这些概念内化为编程本能!
🔗《50节课 Python 从入门到精通》系列课程导航
🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~

5864

被折叠的 条评论
为什么被折叠?



