Python 设计模式全解析
设计模式不是刻板的代码模板,而是被反复验证的解决软件共性问题的思路框架——而 Python 的动态类型、高阶函数、元类等特性,刚好能让这些思路落地得比静态语言更“轻量”,甚至“隐形”地融入日常代码中。
本文通过 Python 原生语法或极简代码实现,快速拆解三类核心设计模式,带你避开“为用模式而用模式”的误区。
完整参考实现库(GitHub 万星+): faif/python-patterns
一、 创建型模式 (Creational Patterns)
核心逻辑: 别让调用方直接 new/创建对象——把创建细节封装起来,既解耦系统与具体类,又能灵活控制实例化的方式、时机、数量。
1. 工厂方法 (Factory Method)
不用显式知道具体类名,通过输入参数/配置环境从工厂函数/方法里“拿”对象。
# Python 可以直接用函数当“工厂”,不需要定义单独的工厂类!
def get_serializer(fmt: str):
"""根据格式字符串返回对应的序列化函数"""
match fmt:
case "json":
return lambda data: f"JSON: {data}"
case "xml":
return lambda data: f"XML: {data}"
case _:
return str # 默认返回字符串序列化
# 调用方完全不用关心具体序列化类的实现
if __name__ == "__main__":
serializer = get_serializer("json")
print(serializer({"name": "Python"})) # JSON: {'name': 'Python'}
- ✅ 优点:屏蔽对象创建细节,新增格式(比如 yaml)只需修改工厂函数,不碰调用方代码
- ❌ 缺点:简单场景下(只有2种可选对象)会增加一点点逻辑成本
- 📌 适用:需根据配置动态选择实例/工具类的场景
2. 抽象工厂 (Abstract Factory)
工厂的“升级款”——负责生产一整套相关/兼容的产品家族(比如一套皮肤里的按钮、背景、字体),保证它们不会“混搭出错”。
from dataclasses import dataclass
# 先定义“产品族”的组成部分
@dataclass
class Button: text: str; theme: str
@dataclass
class Background: color: str
# 具体产品
class DarkButton(Button): theme = "dark"
class LightButton(Button): theme = "light"
class DarkBackground(Background): color = "#1a1a1a"
class LightBackground(Background): color = "#ffffff"
# 抽象工厂的Python极简实现:用类本身当工厂!
class DarkThemeFactory:
@staticmethod
def create_button(text: str) -> Button:
return DarkButton(text=text, theme="dark")
@staticmethod
def create_background() -> Background:
return DarkBackground()
class LightThemeFactory:
@staticmethod
def create_button(text: str) -> Button:
return LightButton(text=text, theme="light")
@staticmethod
def create_background() -> Background:
return LightBackground()
# 使用:只需选择一次工厂,后续所有组件都是配套的
def setup_ui(factory):
btn = factory.create_button("开始")
bg = factory.create_background()
print(f"按钮: {btn}, 背景: {bg}")
setup_ui(DarkThemeFactory)
- ✅ 优点:强制产品族统一风格,不会出现“浅色按钮配黑色背景”的低级错误
- ❌ 缺点:扩展“产品族的维度”(比如新增圆角/直角)很麻烦,要改所有工厂
- 📌 适用:换肤系统、多数据库(MySQL/PostgreSQL)配套连接+查询工具
3. 单例模式 (Singleton)
保证某个类全局只有一个实例,且提供统一的访问入口。Python 实现方式很多,这里用最简单的 __new__ 魔法方法:
class AppConfig:
_instance = None # 私有静态变量存唯一实例
_data = None # 存配置数据
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._data = { # 仅在首次创建时初始化
"api_key": "123456",
"timeout": 30
}
return cls._instance
# 验证单例
cfg1 = AppConfig()
cfg2 = AppConfig()
print(cfg1 is cfg2) # True,是同一个对象
- ✅ 优点:节约内存(比如数据库连接池、全局日志管理器),避免重复初始化
- ❌ 缺点:违背单一职责原则(既管自身创建,又管业务数据),多线程环境下要加锁
- 📌 适用:配置管理、日志系统、全局唯一资源管理器
二、 结构型模式 (Structural Patterns)
核心逻辑: 怎么把类/对象灵活组合起来实现更大的功能,而不是用“继承树”堆得无法维护。
4. 装饰器模式 (Decorator)
Python 自带语法糖 @decorator 完美适配这个模式!动态地给对象/函数“加功能”,不修改其源代码。
# 定义装饰器(用函数当装饰器)
def bold(func):
"""给返回值加加粗标签"""
def wrapper(*args, **kwargs):
return f"<b>{func(*args, **kwargs)}</b>"
return wrapper
def italic(func):
"""给返回值加斜体标签"""
def wrapper(*args, **kwargs):
return f"<i>{func(*args, **kwargs)}</i>"
return wrapper
# 叠加装饰器!顺序很重要:先执行下面的,再执行上面的
@bold
@italic
def say_hello(name: str) -> str:
return f"Hello, {name}!"
# 测试
print(say_hello("Python")) # <b><i>Hello, Python!</i></b>
- ✅ 优点:比继承灵活(可以叠加任意多个装饰器,顺序可调),完全符合开闭原则
- ❌ 缺点:多个嵌套装饰器会让调试变难(比如报错堆栈可能指向 wrapper)
- 📌 适用:日志记录、性能监控、权限校验、缓存
5. 代理模式 (Proxy)
给目标对象加一个“替身”,调用方先访问替身,替身可以控制访问权限、延迟加载目标、缓存结果。
# 目标对象:加载图片很耗时
class RealImage:
def __init__(self, filename: str):
self.filename = filename
self._load_from_disk() # 初始化时就加载
def _load_from_disk(self):
print(f"正在从磁盘加载图片:{self.filename}")
def display(self):
print(f"正在显示图片:{self.filename}")
# 代理对象:延迟加载,只有第一次 display 才加载
class ProxyImage:
def __init__(self, filename: str):
self.filename = filename
self._real_image = None # 初始不加载
def display(self):
if self._real_image is None:
self._real_image = RealImage(self.filename)
self._real_image.display()
# 测试
img = ProxyImage("cat.jpg") # 这里没输出加载信息
print("准备显示图片...")
img.display() # 第一次显示才加载
img.display() # 第二次直接显示,不用再加载
- ✅ 优点:控制访问、延迟加载、缓存结果、隐藏真实对象的实现
- ❌ 缺点:增加了一层中间层,调用速度会有极微小的损耗
- 📌 适用:图片懒加载、远程服务代理、安全控制(检查用户权限后再调用真实对象)
三、 行为型模式 (Behavioral Patterns)
核心逻辑: 关注对象之间怎么通信、怎么分工,让交互更清晰、更易扩展。
6. 迭代器模式 (Iterator)
Python 原生支持!不用手动定义复杂的迭代器类(除非要自定义遍历逻辑)——只要实现 __iter__ 和 __next__ 魔法方法,或者直接用内置的 iter()、next()。
# 1. 内置集合(列表、字典、字符串)都是可迭代的
for num in [1, 2, 3]:
print(num)
# 2. 自定义一个简单的迭代器:遍历斐波那契数列的前N项
class FibonacciIterator:
def __init__(self, n: int):
self.n = n # 前N项
self.a, self.b = 0, 1 # 初始两项
self.count = 0 # 已遍历的数量
def __iter__(self):
return self # 迭代器本身要返回自己
def __next__(self):
if self.count >= self.n:
raise StopIteration # 遍历结束抛出异常
self.count += 1
self.a, self.b = self.b, self.a + self.b
return self.a
# 测试自定义迭代器
for fib in FibonacciIterator(5):
print(fib) # 1 1 2 3 5
- ✅ 优点:访问集合内容而不暴露内部结构,支持多种遍历方式(比如正向/反向遍历树)
- ❌ 缺点:对于简单的列表、字典,手动实现迭代器有点多余
- 📌 适用:遍历数据库查询结果、处理大型流式文件、自定义数据结构
本文只覆盖了最常用的6种设计模式,剩下的可以结合开头的参考代码库深入学习。记住:模式是工具,不是目的——当你遇到问题时,先想“有没有现成的模式能解决”,再决定要不要用,怎么用最 Pythonic。