设计模式与实战应用
High Contrast
Dark Mode
Light Mode
Sepia
Forest
1 min read244 words

设计模式与实战应用

在 Python 中优雅地运用设计模式——用 dataclass 做工厂,用协议做策略,用上下文管理器做资源管理。

常用模式全景

graph TD PATTERNS[Python 设计模式] --> CREATE[创建型] PATTERNS --> STRUCT[结构型] PATTERNS --> BEHAV[行为型] CREATE --> SING[单例] CREATE --> FACT[工厂] CREATE --> BUILD[Builder] STRUCT --> DECO[装饰器] STRUCT --> ADAPT[适配器] STRUCT --> PROXY[代理] BEHAV --> STRAT[策略] BEHAV --> OBS[观察者] BEHAV --> CTX[上下文管理] style PATTERNS fill:#e3f2fd,stroke:#1565c0,stroke-width:2px style BEHAV fill:#c8e6c9,stroke:#388e3c,stroke-width:2px

单例模式

"""
单例模式:确保全局只有一个实例
"""
# 方法 1:模块级变量(最 Pythonic)
# config.py
class _Config:
def __init__(self):
self.debug = False
self.db_url = "sqlite:///app.db"
config = _Config()  # 模块导入时创建,天然单例
# 方法 2:__new__ 控制实例化
class Database:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, url: str = "sqlite:///default.db"):
if not hasattr(self, "_initialized"):
self.url = url
self._initialized = True
db1 = Database("postgres://...")
db2 = Database("mysql://...")
print(db1 is db2)    # True
print(db1.url)       # postgres://...(第一次的值)

工厂模式

"""
工厂模式:根据条件创建不同对象
"""
from dataclasses import dataclass
@dataclass
class Notification:
to: str
message: str
channel: str
def send(self):
print(f"[{self.channel}] → {self.to}: {self.message}")
def create_notification(channel: str, to: str, msg: str) -> Notification:
"""简单工厂"""
handlers = {
"email": lambda: Notification(to, msg, "Email"),
"sms": lambda: Notification(to, msg, "SMS"),
"push": lambda: Notification(to, msg, "Push"),
}
creator = handlers.get(channel)
if creator is None:
raise ValueError(f"不支持的渠道: {channel}")
return creator()
# 使用
notif = create_notification("email", "user@test.com", "验证码: 1234")
notif.send()  # [Email] → user@test.com: 验证码: 1234

策略模式

"""
策略模式:用 Protocol 定义接口,运行时切换算法
"""
from typing import Protocol
from dataclasses import dataclass
class PricingStrategy(Protocol):
"""定价策略接口"""
def calculate(self, base_price: float) -> float: ...
class RegularPricing:
def calculate(self, base_price: float) -> float:
return base_price
class VIPPricing:
discount: float = 0.8
def calculate(self, base_price: float) -> float:
return base_price * self.discount
class PromoPricing:
def __init__(self, discount: float):
self.discount = discount
def calculate(self, base_price: float) -> float:
return base_price * self.discount
@dataclass
class Order:
items: list[tuple[str, float]]
strategy: PricingStrategy
def total(self) -> float:
subtotal = sum(price for _, price in self.items)
return self.strategy.calculate(subtotal)
# 使用
order = Order(
items=[("Python Book", 59.0), ("Coffee", 15.0)],
strategy=VIPPricing(),
)
print(f"VIP 价: ¥{order.total():.2f}")  # ¥59.20
order.strategy = PromoPricing(0.5)
print(f"半价: ¥{order.total():.2f}")    # ¥37.00

观察者模式

"""
观察者模式:事件驱动编程
"""
from collections import defaultdict
from typing import Callable
class EventBus:
"""事件总线"""
def __init__(self):
self._handlers: dict[str, list[Callable]] = defaultdict(list)
def on(self, event: str, handler: Callable):
"""注册事件处理器"""
self._handlers[event].append(handler)
def emit(self, event: str, **kwargs):
"""触发事件"""
for handler in self._handlers[event]:
handler(**kwargs)
# 使用
bus = EventBus()
bus.on("user_signup", lambda name, **_: print(f"发送欢迎邮件给 {name}"))
bus.on("user_signup", lambda name, **_: print(f"创建默认配置给 {name}"))
bus.on("order_paid", lambda order_id, **_: print(f"订单 {order_id} 已发货"))
bus.emit("user_signup", name="Alice")
# 发送欢迎邮件给 Alice
# 创建默认配置给 Alice

上下文管理器

"""
上下文管理器:优雅的资源管理
"""
from contextlib import contextmanager
import time
# 方法 1:类实现
class Timer:
"""性能计时器"""
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, *exc):
self.elapsed = time.perf_counter() - self.start
print(f"耗时: {self.elapsed:.4f}s")
return False  # 不吞异常
with Timer() as t:
sum(range(1_000_000))
# 耗时: 0.0234s
# 方法 2:装饰器(更简洁)
@contextmanager
def temp_directory():
"""临时目录,退出时自动清理"""
import tempfile
import shutil
tmpdir = tempfile.mkdtemp()
try:
yield tmpdir
finally:
shutil.rmtree(tmpdir)
with temp_directory() as d:
print(f"临时目录: {d}")
# 退出 with 后目录已删除

模式选择指南

场景 推荐模式 Python 实现方式
全局唯一配置 单例 模块变量
根据条件创建对象 工厂 函数 + dict 映射
运行时切换算法 策略 Protocol + dataclass
事件通知 观察者 EventBus / signal
资源管理 上下文管理 __enter__/__exit__@contextmanager
增强函数行为 装饰器 @decorator

本章小结

知识点 要点
单例 模块变量或 __new__ 控制
工厂 dict 映射 + lambda
策略 Protocol 接口 + 依赖注入
观察者 EventBus 发布/订阅
上下文管理 with 语句自动清理资源

下一章:函数式编程——高阶函数与装饰器,Python 的另一面。