30天入门Python(基础篇)------第29天:Python面向对象之多态与抽象类
📅 学习日期 :第29天 | ⏱️ 预计用时 :50分钟 | 📊 难度等级:⭐⭐⭐⭐
🎯 学习目标
- 理解多态的概念和意义
- 掌握鸭子类型(Duck Typing)的思想
- 学会使用
abc模块创建抽象类 - 理解抽象方法和具体方法的区别
- 掌握
@abstractmethod和@abstractproperty - 了解接口与实现分离的设计思想
- 能够设计可扩展的面向对象架构
一、什么是多态?
1.1 多态的概念
多态(Polymorphism) 来自希腊语,意思是"多种形态"。在面向对象编程中,多态指的是:
同一个方法调用,可以根据对象的不同,表现出不同的行为。
简单说就是:一种接口,多种实现。
1.2 生活类比
- "吃"这个动作:人用筷子吃,狗用嘴吃,鸟用喙吃。接口都是"吃",但实现方式不同。
- "支付"功能 :微信支付、支付宝支付、银行卡支付。用户只调用
pay(),底层实现各不相同。
1.3 多态示例
python
class Dog:
def speak(self):
return "汪汪!"
class Cat:
def speak(self):
return "喵喵!"
class Duck:
def speak(self):
return "嘎嘎!"
# 多态:统一接口,不同实现
def animal_sound(animal):
print(animal.speak())
animals = [Dog(), Cat(), Duck()]
for animal in animals:
animal_sound(animal)
# 输出:
# 汪汪!
# 喵喵!
# 嘎嘎!
关键点 :animal_sound() 函数不需要知道传入的是什么具体类型的对象,只要它有 speak() 方法就行。
二、Python 中的鸭子类型
2.1 什么是鸭子类型?
如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子。
Python 不关心对象的具体类型,只关心它有没有所需的方法和属性。这是 Python 的"动态类型"特性。
python
class Car:
def start(self):
print("汽车启动")
class Bike:
def start(self):
print("自行车启动")
class Boat:
def start(self):
print("船启动")
def start_vehicle(vehicle):
"""不需要检查类型,只要有 start 方法就行"""
vehicle.start()
# 三种不同类型的对象都能使用
start_vehicle(Car()) # 汽车启动
start_vehicle(Bike()) # 自行车启动
start_vehicle(Boat()) # 船启动
2.2 鸭子类型的优势
- 灵活:不需要继承同一个父类
- 简洁 :不需要
isinstance()检查 - 可扩展:新类只要实现相应方法就能使用
2.3 鸭子类型的陷阱
python
class Bird:
def fly(self):
print("鸟在飞")
class Plane:
def fly(self):
print("飞机在飞")
class Stone:
pass # 没有 fly 方法
def make_it_fly(thing):
thing.fly()
make_it_fly(Bird()) # ✅ 鸟在飞
make_it_fly(Plane()) # ✅ 飞机在飞
# make_it_fly(Stone()) # ❌ AttributeError: 'Stone' object has no attribute 'fly'
解决方案:使用抽象类或协议来约束行为。
三、抽象类
3.1 为什么需要抽象类?
考虑一个支付系统:
python
class Payment:
def pay(self, amount):
raise NotImplementedError("子类必须实现 pay 方法")
class WeChatPay(Payment):
def pay(self, amount):
print(f"微信支付: {amount} 元")
class Alipay(Payment):
def pay(self, amount):
print(f"支付宝支付: {amount} 元")
class BankCard(Payment):
# 忘记实现 pay 方法
pass
# 运行时才发现错误
payment = BankCard()
payment.pay(100) # ❌ NotImplementedError
问题:错误在运行时 才被发现。使用抽象类可以在实例化时就报错。
3.2 使用 abc 模块创建抽象类
python
from abc import ABC, abstractmethod
class Payment(ABC):
"""支付抽象基类"""
@abstractmethod
def pay(self, amount):
"""支付方法,子类必须实现"""
pass
def get_payment_info(self):
"""非抽象方法,子类可以直接使用"""
return f"支付金额: {amount} 元"
class WeChatPay(Payment):
def pay(self, amount):
print(f"微信支付: {amount} 元")
class Alipay(Payment):
def pay(self, amount):
print(f"支付宝支付: {amount} 元")
class BankCard(Payment):
pass # 没有实现 pay 方法
# 测试
wechat = WeChatPay()
wechat.pay(100) # ✅ 微信支付: 100 元
# alipay = Alipay()
# alipay.pay(200) # ✅ 支付宝支付: 200 元
# bank_card = BankCard() # ❌ TypeError: Can't instantiate abstract class BankCard
3.3 抽象类的核心规则
| 规则 | 说明 |
|---|---|
必须继承 ABC |
使用 class MyAbstractClass(ABC): |
抽象方法用 @abstractmethod |
子类必须实现,否则无法实例化 |
| 可以有具体方法 | 抽象类中可以定义非抽象方法 |
| 不能直接实例化 | AbstractClass() 会报错 |
| 子类必须实现所有抽象方法 | 否则子类也是抽象类 |
四、抽象类的高级用法
4.1 抽象属性
python
from abc import ABC, abstractmethod
class Shape(ABC):
"""图形抽象基类"""
@property
@abstractmethod
def area(self):
"""面积,子类必须实现"""
pass
@property
@abstractmethod
def perimeter(self):
"""周长,子类必须实现"""
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
@property
def area(self):
import math
return math.pi * self.radius ** 2
@property
def perimeter(self):
import math
return 2 * math.pi * self.radius
circle = Circle(5)
print(f"面积: {circle.area:.2f}") # 78.54
print(f"周长: {circle.perimeter:.2f}") # 31.42
4.2 抽象静态方法和类方法
python
from abc import ABC, abstractmethod
class DataSource(ABC):
@staticmethod
@abstractmethod
def get_connection_string():
"""获取连接字符串"""
pass
@classmethod
@abstractmethod
def from_config(cls, config):
"""从配置创建实例"""
pass
class MySQLDataSource(DataSource):
def __init__(self, host, port, db):
self.host = host
self.port = port
self.db = db
@staticmethod
def get_connection_string():
return "mysql+pymysql://..."
@classmethod
def from_config(cls, config):
return cls(config["host"], config["port"], config["db"])
4.3 强制子类实现
python
from abc import ABC, abstractmethod
class Logger(ABC):
@abstractmethod
def log(self, message):
"""记录日志"""
pass
@abstractmethod
def clear(self):
"""清空日志"""
pass
def log_multiple(self, messages):
"""非抽象方法,使用子类实现的 log"""
for msg in messages:
self.log(msg)
class FileLogger(Logger):
def __init__(self, filename):
self.filename = filename
self.messages = []
def log(self, message):
self.messages.append(message)
print(f"[FILE] {message}")
def clear(self):
self.messages.clear()
print("日志已清空")
logger = FileLogger("app.log")
logger.log("系统启动")
logger.log("用户登录")
logger.log_multiple(["操作1", "操作2"])
logger.clear()
五、接口与实现分离
5.1 Python 中的"接口"
Python 没有 Java/C# 那样的 interface 关键字,但可以通过只包含抽象方法的抽象类模拟接口:
python
from abc import ABC, abstractmethod
class Serializable(ABC):
"""序列化接口"""
@abstractmethod
def to_dict(self):
"""转换为字典"""
pass
@classmethod
@abstractmethod
def from_dict(cls, data):
"""从字典创建对象"""
pass
class Comparable(ABC):
"""比较接口"""
@abstractmethod
def compare_to(self, other):
"""比较两个对象"""
pass
class Student(Serializable, Comparable):
"""实现多个接口"""
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def to_dict(self):
return {"name": self.name, "age": self.age, "score": self.score}
@classmethod
def from_dict(cls, data):
return cls(data["name"], data["age"], data["score"])
def compare_to(self, other):
if self.score > other.score:
return 1
elif self.score < other.score:
return -1
return 0
# 使用
stu1 = Student("小明", 20, 95)
stu2 = Student("小红", 21, 88)
print(stu1.to_dict()) # {'name': '小明', 'age': 20, 'score': 95}
print(stu1.compare_to(stu2)) # 1(小明成绩更高)
5.2 策略模式(多态的经典应用)
python
from abc import ABC, abstractmethod
class SortStrategy(ABC):
"""排序策略接口"""
@abstractmethod
def sort(self, data):
pass
class BubbleSort(SortStrategy):
def sort(self, data):
arr = data.copy()
n = len(arr)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
class QuickSort(SortStrategy):
def sort(self, data):
if len(data) <= 1:
return data
pivot = data[len(data) // 2]
left = [x for x in data if x < pivot]
middle = [x for x in data if x == pivot]
right = [x for x in data if x > pivot]
return self.sort(left) + middle + self.sort(right)
class Sorter:
"""排序器(使用策略模式)"""
def __init__(self, strategy: SortStrategy):
self.strategy = strategy
def sort(self, data):
return self.strategy.sort(data)
# 使用
data = [64, 34, 25, 12, 22, 11, 90]
# 使用冒泡排序
bubble_sorter = Sorter(BubbleSort())
print(f"冒泡排序: {bubble_sorter.sort(data)}")
# 使用快速排序
quick_sorter = Sorter(QuickSort())
print(f"快速排序: {quick_sorter.sort(data)}")
# 随时可以切换策略
bubble_sorter.strategy = QuickSort()
print(f"切换策略后: {bubble_sorter.sort(data)}")
六、观察者模式(多态实战)
python
from abc import ABC, abstractmethod
class Observer(ABC):
"""观察者接口"""
@abstractmethod
def update(self, event, data):
"""接收到通知时的处理"""
pass
class Subject:
"""主题(被观察者)"""
def __init__(self):
self._observers = []
def add_observer(self, observer: Observer):
self._observers.append(observer)
def remove_observer(self, observer: Observer):
self._observers.remove(observer)
def notify(self, event, data):
for observer in self._observers:
observer.update(event, data)
# 具体观察者
class EmailNotifier(Observer):
def update(self, event, data):
print(f"📧 邮件通知: {event} - {data}")
class SMSNotifier(Observer):
def update(self, event, data):
print(f"📱 短信通知: {event} - {data}")
class LogRecorder(Observer):
def update(self, event, data):
print(f"📝 日志记录: {event} - {data}")
# 使用
subject = Subject()
subject.add_observer(EmailNotifier())
subject.add_observer(SMSNotifier())
subject.add_observer(LogRecorder())
subject.notify("新用户注册", "用户ID: 12345")
subject.notify("订单完成", "订单号: 2026001")
七、isinstance() 与抽象类
python
from abc import ABC, abstractmethod
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
class Circle(Drawable):
def draw(self):
print("绘制圆形")
class Line(Drawable):
def draw(self):
print("绘制直线")
# isinstance 检查
shape = Circle()
print(isinstance(shape, Drawable)) # True
# 可以安全地调用抽象方法
if isinstance(shape, Drawable):
shape.draw() # 绘制圆形
八、实战项目:插件系统
python
from abc import ABC, abstractmethod
import json
class Plugin(ABC):
"""插件接口"""
@property
@abstractmethod
def name(self):
"""插件名称"""
pass
@property
@abstractmethod
def version(self):
"""插件版本"""
pass
@abstractmethod
def execute(self, data):
"""执行插件功能"""
pass
class JSONFormatter(Plugin):
"""JSON 格式化插件"""
@property
def name(self):
return "JSON格式化"
@property
def version(self):
return "1.0"
def execute(self, data):
return json.dumps(data, ensure_ascii=False, indent=2)
class CSVFormatter(Plugin):
"""CSV 格式化插件"""
@property
def name(self):
return "CSV格式化"
@property
def version(self):
return "1.0"
def execute(self, data):
if not data:
return ""
headers = data[0].keys()
lines = [",".join(headers)]
for item in data:
lines.append(",".join(str(item[h]) for h in headers))
return "\n".join(lines)
class PluginManager:
"""插件管理器"""
def __init__(self):
self.plugins = {}
def register(self, plugin: Plugin):
self.plugins[plugin.name] = plugin
def execute(self, plugin_name, data):
if plugin_name not in self.plugins:
raise ValueError(f"插件 '{plugin_name}' 不存在")
return self.plugins[plugin_name].execute(data)
# 使用
data = [
{"name": "小明", "age": 20, "score": 95},
{"name": "小红", "age": 21, "score": 88},
]
manager = PluginManager()
manager.register(JSONFormatter())
manager.register(CSVFormatter())
print(manager.execute("JSON格式化", data))
print(manager.execute("CSV格式化", data))
九、总结
| 概念 | 说明 |
|---|---|
| 多态 | 同一接口,不同实现 |
| 鸭子类型 | 关心行为,不关心类型 |
| 抽象类 | 使用 ABC + @abstractmethod 定义 |
| 抽象方法 | 子类必须实现的方法 |
| 抽象属性 | 使用 @property + @abstractmethod |
| 接口 | 只含抽象方法的抽象类 |
| 策略模式 | 运行时切换算法 |
| 观察者模式 | 一对多的依赖通知 |
最佳实践
- 优先使用鸭子类型 ,而不是
isinstance()检查 - 抽象类定义契约,确保子类实现必要方法
- 策略模式是切换算法的首选方案
- 避免"if-else 判断类型",用多态替代
- 接口应该小而专一,不要大而全
十、练习题
练习 1:数据库连接抽象类
创建 DatabaseConnection 抽象类,定义 connect()、execute()、close() 方法,实现 MySQL 和 SQLite 两个具体类。
练习 2:命令模式
创建 Command 接口,实现 CreateFileCommand、DeleteFileCommand,支持撤销操作。
练习 3:模板方法模式
创建 DataProcessor 抽象类,定义 process() 模板方法(包含 read、transform、save 三个步骤),其中 read 和 save 是抽象方法,transform 有默认实现。
参考答案
练习 3 参考答案
python
from abc import ABC, abstractmethod
class DataProcessor(ABC):
@abstractmethod
def read(self):
pass
def transform(self, data):
"""默认转换:返回原始数据"""
return data
@abstractmethod
def save(self, data):
pass
def process(self):
"""模板方法:固定流程"""
print("开始处理...")
data = self.read()
print(f"读取数据: {data}")
data = self.transform(data)
print(f"转换数据: {data}")
self.save(data)
print("处理完成")
class CSVProcessor(DataProcessor):
def read(self):
return [{"name": "Alice"}, {"name": "Bob"}]
def save(self, data):
print(f"保存 CSV: {data}")
class JSONProcessor(DataProcessor):
def read(self):
return {"users": ["Alice", "Bob"]}
def transform(self, data):
return list(data.get("users", []))
def save(self, data):
print(f"保存 JSON: {data}")
csv_processor = CSVProcessor()
csv_processor.process()
json_processor = JSONProcessor()
json_processor.process()
🎉 恭喜你完成了第29天的学习!多态和抽象类让代码更加灵活、可扩展。
📌 下一天预告:第30天 ------ Python面向对象之类属性与方法(私有方法、私有属性)------ 基础篇最后一天!