Python设计模式综合实践

GoF设计模式的Pythonic实现

主题: GoF 23种设计模式 + Python特有设计模式的Pythonic实现

核心原则: 设计模式是解决特定问题的成熟方案,但应利用Python动态特性简化实现

主要内容: 创建型模式Python简化、结构型模式动态优化、行为型模式函数式简化、Python特有模式、模式选择指南

关键词: Python, 设计模式, GoF, Pythonic, 创建型, 结构型, 行为型, 模式选择

一、引言:设计模式与Python哲学

设计模式(Design Patterns)源自GoF(Gang of Four)的经典著作《Design Patterns: Elements of Reusable Object-Oriented Software》。这23种模式并非凭空创造的规则,而是对反复出现的软件设计问题的成熟解决方案的总结。然而,GoF模式最初基于C++和Smalltalk等静态类型语言的实践,在Python这样的动态语言中,许多模式的实现可以大幅简化,甚至完全内化为语言特性。

Python之禅(Zen of Python)中的核心理念 —— "Simple is better than complex""There should be one -- and preferably only one -- obvious way to do it" —— 应该成为我们在Python中应用设计模式的指导原则。不要为了用模式而用模式,当Python语言自身已提供了更简洁的解决方案时,应优先使用Pythonic的方式。

核心观点:

  • 设计模式是解决方案的命名与归纳,而非创造新规则
  • Python的动态特性(一等函数、元类、描述符协议、上下文管理器)可大幅简化模式实现
  • 不要为了模式而模式,Pythonic优先于"纯正"的GoF实现
  • 理解模式的本质比记忆UML图更重要

二、创建型模式的Python简化

创建型模式关注对象创建机制,试图以灵活的方式创建对象,隐藏创建逻辑。在Python中,由于语言本身支持关键字参数、类方法、模块单例等特性,创建型模式的实现变得异常简洁。

2.1 单例模式(Singleton)

单例模式确保一个类只有一个实例,并提供全局访问点。在静态语言中通常需要私有构造函数和静态方法,但Python提供了多种更简洁的方式。

方式一:模块单例(最Pythonic)

Python模块天然是单例的,导入一次后即被缓存。这是最推荐的方式。

# singleton_module.py -- 模块级单例,最Pythonic的方式 # Python模块在首次导入后即被sys.modules缓存,后续导入返回同一对象 class DatabaseConnection: def __init__(self, host, port): self.host = host self.port = port self._connected = False def connect(self): self._connected = True return f"Connected to {self.host}:{self.port}" # 在模块作用域直接实例化,全局唯一 db = DatabaseConnection("localhost", 5432)

方式二:使用装饰器

利用装饰器封装单例逻辑,对任何类即插即用。

from functools import wraps def singleton(cls): """单例装饰器:确保类只有一个实例""" instances = {} @wraps(cls) def get_instance(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class AppConfig: def __init__(self): self.settings = {} # 验证单例 a = AppConfig() b = AppConfig() assert a is b # True
# 方式三:使用元类(适合框架级实现) class SingletonMeta(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super().__call__(*args, **kwargs) return cls._instances[cls] class Logger(metaclass=SingletonMeta): def __init__(self): self.logs = [] def log(self, msg): self.logs.append(msg)

Pythonic建议

模块单例是首选方案,因为它利用了语言内置机制,无需额外代码。装饰器方式适合需要对多个类复用单例逻辑的场景。元类方式最重量级,仅在框架或库开发中需要干预类创建过程时使用。

2.2 工厂模式(Factory)

工厂模式将对象创建逻辑封装起来,客户端无需知道具体类名。Python的动态类型和关键字参数使得工厂模式异常简洁。

# 简单工厂 -- 用函数即可,无需工厂类 from enum import Enum, auto class AnimalType(Enum): DOG = auto() CAT = auto() DUCK = auto() class Dog: def speak(self): return "Woof!" class Cat: def speak(self): return "Meow!" class Duck: def speak(self): return "Quack!" def create_animal(animal_type: AnimalType): """简单工厂函数 -- 在Python中函数就是一等的工厂""" mapping = { AnimalType.DOG: Dog, AnimalType.CAT: Cat, AnimalType.DUCK: Duck, } return mapping[animal_type]() # 使用 -- 客户端无需知道具体类 animal = create_animal(AnimalType.DOG) print(animal.speak()) # Woof!

工厂方法 vs 抽象工厂

在GoF中,工厂方法通过子类重写来创建不同产品,抽象工厂用于创建产品族。Python中利用类方法和模块级函数可以大幅简化这两者。

# 工厂方法 -- 使用类方法,Python原生支持 class Serializer: @classmethod def create(cls, fmt: str): """类方法作为工厂方法""" subclasses = {sub.__name__.lower(): sub for sub in cls.__subclasses__()} return subclasses.get(fmt, cls)() def serialize(self, data): return str(data) class JsonSerializer(Serializer): def serialize(self, data): import json return json.dumps(data) class XmlSerializer(Serializer): def serialize(self, data): # XML序列化实现... return f"<data>{data}</data>" # 使用 -- 自动发现子类 serializer = Serializer.create("jsonserializer") print(serializer.serialize({"key": "value"}))
# 抽象工厂 -- 使用字典作为注册表,比类层次更灵活 class GUIFactory: """使用注册表的抽象工厂 -- Pythonic方式""" _factories = {} @classmethod def register(cls, name): """装饰器注册工厂""" def wrapper(factory_cls): cls._factories[name] = factory_cls return factory_cls return wrapper @classmethod def create(cls, name, theme): factory = cls._factories.get(name) if not factory: raise ValueError(f"Unknown factory: {name}") return factory(theme) @GUIFactory.register("windows") class WindowsFactory: def __init__(self, theme): self.theme = theme def create_button(self): return f"Windows [{self.theme}] Button" def create_menu(self): return f"Windows [{self.theme}] Menu" @GUIFactory.register("mac") class MacFactory: def __init__(self, theme): self.theme = theme def create_button(self): return f"macOS [{self.theme}] Button" def create_menu(self): return f"macOS [{self.theme}] Menu" # 使用 factory = GUIFactory.create("windows", "dark") print(factory.create_button()) # Windows [dark] Button

2.3 建造者模式(Builder)

建造者模式用于构建复杂对象,分离构建过程与表示。Python的特性使得这一模式可以大幅简化:命名参数、链式调用、dataclass等。

# Pythonic建造者 -- 链式调用 + dataclass from dataclasses import dataclass, field @dataclass class Computer: """最终产品 -- dataclass自动生成__init__和__repr__""" cpu: str = "Standard CPU" ram_gb: int = 8 storage_gb: int = 256 gpu: str = "Integrated" os: str = "Linux" has_wifi: bool = True extras: list = field(default_factory=list) class ComputerBuilder: """建造者 -- 通过链式调用提供流畅的构建体验""" def __init__(self): self.computer = Computer() def cpu(self, model: str): self.computer.cpu = model; return self def ram(self, gb: int): self.computer.ram_gb = gb; return self def storage(self, gb: int): self.computer.storage_gb = gb; return self def gpu(self, model: str): self.computer.gpu = model; return self def os(self, name: str): self.computer.os = name; return self def add_extra(self, item: str): self.computer.extras.append(item); return self def build(self) -> Computer: return self.computer # 使用 -- 流畅的链式接口 gaming_pc = (ComputerBuilder() .cpu("Intel i9-13900K") .ram(64) .storage(2000) .gpu("NVIDIA RTX 4090") .os("Windows 11") .add_extra("RGB Lighting") .build()) print(gaming_pc)

Pythonic简化

在许多场景下,直接使用命名参数dataclass就足以替代完整的建造者模式。建造者模式的真正价值在于当构建过程涉及多个步骤、参数之间有复杂的依赖关系,或需要创建不同的产品变体时。

2.4 原型模式(Prototype)

原型模式通过复制已有对象来创建新对象,避免重复的初始化开销。Python的copy模块提供了原生支持。

# 原型模式 -- Python的copy模块原生支持 import copy from dataclasses import dataclass, field @dataclass class WebPage: title: str content: str styles: list = field(default_factory=list) scripts: list = field(default_factory=list) meta_tags: dict = field(default_factory=dict) def clone(self, **updates): """克隆并应用更新 -- 结合原型+建造者思路""" new = copy.deepcopy(self) for key, value in updates.items(): setattr(new, key, value) return new # 创建原型实例 base_page = WebPage( title="Base", content="Default content", styles=["bootstrap.css", "main.css"], scripts=["jquery.js", "app.js"], meta_tags={"charset": "utf-8", "viewport": "responsive"} ) # 通过原型克隆创建不同页面 -- 无需重新初始化 home_page = base_page.clone(title="Home", content="Welcome!") about_page = base_page.clone(title="About Us", content="Our story") contact_page = base_page.clone(title="Contact", content="Get in touch", scripts=["jquery.js", "app.js", "map.js"]) print(home_page.title) # Home print(about_page.styles) # ['bootstrap.css', 'main.css'] -- 共享样式

创建型模式总结

  • Python模块本身就是单例,无需额外实现
  • 一等函数和字典注册表使工厂模式比类继承更灵活
  • dataclass + 链式调用简化建造者模式
  • copy模块原生支持原型模式

三、结构型模式的动态特性优化

结构型模式关注类和对象的组合,通过继承或组合构建更大的结构。Python的动态特性(鸭子类型、__getattr__、装饰器)让结构型模式更加灵活简洁。

3.1 适配器模式(Adapter)

适配器模式让接口不兼容的类能够协作。Python的鸭子类型特性使得适配器实现异常简洁 —— 只要对象有正确的方法,就能被使用。

# 类适配器 -- 通过继承 + 组合实现 class USBDevice: """客户端期望的接口""" def connect_via_usb(self): pass def transfer_data(self, data): pass class HDMICable: """需要适配的类 -- 接口不兼容""" def plug_hdmi(self): return "HDMI plugged" def send_signal(self, signal): return f"Sending signal: {signal} via HDMI" class HDMItoUSBAdapter(USBDevice): """适配器:将HDMI接口适配为USB接口""" def __init__(self, hdmi_device: HDMICable): self._hdmi = hdmi_device def connect_via_usb(self): return self._hdmi.plug_hdmi() def transfer_data(self, data): return self._hdmi.send_signal(data) # Pythonic方式:直接包装(鸭子类型 -- 不需要显式继承USBDevice) def use_usb_device(device): """任何拥有connect_via_usb和transfer_data方法的对象都可以使用""" device.connect_via_usb() return device.transfer_data("hello") # 匿名适配器 -- 函数式适配,最Pythonic hdmi = HDMICable() adapter = type('AnonymousAdapter', (), { 'connect_via_usb': hdmi.plug_hdmi, 'transfer_data': lambda data: hdmi.send_signal(data), }) print(use_usb_device(adapter))

3.2 装饰器模式(Decorator)

装饰器模式动态地给对象添加额外职责。在Python中,这个模式已经上升为语言特性 —— 函数装饰器和类装饰器。这是GoF模式与Python语言完美融合的最佳例子。

# Python装饰器 = 语言内置的装饰器模式 from functools import wraps import time def log_execution_time(func): """函数装饰器:记录函数执行时间 -- GoF装饰器模式的Pythonic实现""" @wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) elapsed = time.perf_counter() - start print(f"[TIMING] {func.__name__} took {elapsed:.4f}s") return result return wrapper def retry(max_attempts=3, delay=0.1): """带参数的装饰器:重试逻辑""" def decorator(func): @wraps(func) def wrapper(*args, **kwargs): for attempt in range(1, max_attempts + 1): try: return func(*args, **kwargs) except Exception as e: if attempt == max_attempts: raise print(f"[RETRY] Attempt {attempt} failed: {e}") time.sleep(delay) return None return wrapper return decorator @log_execution_time @retry(max_attempts=2) def unreliable_api_call(url): """装饰器可以叠加使用 -- 多个职责组合""" if time.time() % 2 < 0.5: raise ConnectionError("Network timeout") return f"Data from {url}"
# 类装饰器 -- 应用于整个类的装饰器 def add_str_repr(cls): """类装饰器:为类添加__str__和__repr__方法""" def __str__(self): items = sorted(vars(self).items()) return f"{cls.__name__}({', '.join(f'{k}={v}' for k, v in items)})" cls.__str__ = __str__ cls.__repr__ = __str__ cls.__name__ = cls.__name__ return cls @add_str_repr class Book: def __init__(self, title, author, year): self.title = title self.author = author self.year = year print(Book("Python Cookbook", "David Beazley", 2013)) # Book(title=Python Cookbook, author=David Beazley, year=2013)

3.3 外观模式(Facade)

外观模式为复杂子系统提供统一的高层接口。这是Python中极其常用的模式(尤其在库设计中)。

# 外观模式 -- 为复杂系统提供简洁接口 class VideoFile: def __init__(self, path): self.path = path self.codec = self._detect_codec() def _detect_codec(self): return "h264" if self.path.endswith(".mp4") else "hevc" class AudioExtractor: def extract(self, file): return f"audio from {file.path}" class SubtitleParser: def parse(self, file): return f"subtitles from {file.path}" class Transcoder: def transcode(self, file, target): return f"transcoded to {target}" class VideoConverter: """外观 -- 为视频转换提供统一接口""" def __init__(self): self._audio_extractor = AudioExtractor() self._subtitle_parser = SubtitleParser() self._transcoder = Transcoder() def convert(self, source_path, target_format): """统一接口:客户端只需调用这一个方法""" file = VideoFile(source_path) audio = self._audio_extractor.extract(file) subs = self._subtitle_parser.parse(file) result = self._transcoder.transcode(file, target_format) return { "source": source_path, "format": target_format, "audio": audio, "subtitles": subs, "result": result, } # 客户端只需知道外观接口 converter = VideoConverter() result = converter.convert("movie.mp4", "avi") print(result["result"]) # transcoded to avi

3.4 代理模式(Proxy)

代理模式为其他对象提供代理以控制对该对象的访问。Python的__getattr__和描述符协议让代理实现更加优雅。

# 代理模式 -- 利用__getattr__自动转发 class LazyImage: """虚拟代理 -- 延迟加载大图""" def __init__(self, path): self.path = path self._real_image = None def _load_image(self): print(f"[LOAD] Loading image from {self.path}") self._real_image = RealImage(self.path) def display(self): """首次调用时加载,后续复用""" if not self._real_image: self._load_image() return self._real_image.display() class RealImage: def __init__(self, path): self.path = path def display(self): return f"Displaying image: {self.path}" # 保护代理 -- 使用描述符实现访问控制 class AccessControl: """描述符:实现属性级别的访问控制""" def __init__(self, permission): self.permission = permission self._value = None def __get__(self, obj, objtype=None): if not obj or not getattr(obj, '_user', None): raise PermissionError("No user set") if not hasattr(obj._user, self.permission): raise PermissionError(f"Need {self.permission} permission") return self._value def __set__(self, obj, value): self._value = value

3.5 组合模式(Composite)

组合模式将对象组合成树形结构以表示"部分-整体"的层次结构。Python的鸭子类型和协议让组合模式实现非常自然。

# 组合模式 -- 统一的迭代接口 from abc import ABC, abstractmethod class FileSystemNode(ABC): """抽象组件""" @abstractmethod def get_size(self) -> int: pass @abstractmethod def list(self, indent="") -> str: pass class File(FileSystemNode): """叶子节点""" def __init__(self, name, size): self.name = name self._size = size def get_size(self): return self._size def list(self, indent=""): return f"{indent}📄 {self.name} ({self._size} bytes)" class Directory(FileSystemNode): """复合节点 -- 可以包含叶子和其他复合节点""" def __init__(self, name): self.name = name self._children = [] def add(self, node: FileSystemNode): self._children.append(node) return self # 链式调用 def remove(self, node: FileSystemNode): self._children.remove(node) def get_size(self): return sum(child.get_size() for child in self._children) def list(self, indent=""): lines = [f"{indent}📁 {self.name}/ ({self.get_size()} bytes)"] for child in self._children: lines.append(child.list(indent + " ")) return "\n".join(lines) # 构建文件系统树 root = Directory("project") src = Directory("src") root.add(src) src.add(File("main.py", 1024)).add(File("utils.py", 512)) root.add(File("README.md", 256)) print(root.list()) # 📁 project/ (1792 bytes) # 📁 src/ (1536 bytes) # 📄 main.py (1024 bytes) # 📄 utils.py (512 bytes) # 📄 README.md (256 bytes) print(f"Total size: {root.get_size()} bytes") # 1792

3.6 桥接模式(Bridge)

桥接模式将抽象部分与实现部分分离。Python的鸭子类型使得桥接异常简洁。

# 桥接模式 -- 抽象与实现分离 # 实现层次:不同渲染引擎 class VectorRenderer: def render_circle(self, radius): return f"Vector: Drawing circle with radius {radius}" def render_square(self, side): return f"Vector: Drawing square with side {side}" class RasterRenderer: def render_circle(self, radius): return f"Raster: Drawing circle as pixels, radius={radius}" def render_square(self, side): return f"Raster: Drawing square as pixels, side={side}" # 抽象层次:不同形状 class Shape: """桥接抽象 -- 持有渲染器的引用""" def __init__(self, renderer): self.renderer = renderer def draw(self): pass def resize(self, factor): pass class Circle(Shape): def __init__(self, renderer, radius): super().__init__(renderer) self.radius = radius def draw(self): return self.renderer.render_circle(self.radius) def resize(self, factor): self.radius *= factor # 使用 -- 自由组合抽象和实现 vector_circle = Circle(VectorRenderer(), 5) raster_circle = Circle(RasterRenderer(), 10) print(vector_circle.draw()) # Vector: Drawing circle with radius 5 print(raster_circle.draw()) # Raster: Drawing circle as pixels...

3.7 享元模式(Flyweight)

享元模式通过共享大量细粒度对象来节省内存。Python的functools.lru_cache和共享引用让享元实现非常直接。

# 享元模式 -- 使用缓存共享对象 from functools import lru_cache class CharacterStyle: """享元对象 -- 内部状态(可共享)""" def __init__(self, font, size, bold, italic, color): self.font = font self.size = size self.bold = bold self.italic = italic self.color = color def __repr__(self): return f"Style({self.font}, {self.size}, bold={self.bold})" class StyleFactory: """享元工厂 -- 缓存和复用样式对象""" _cache = {} @classmethod def get_style(cls, font, size, bold, italic, color): key = (font, size, bold, italic, color) if key not in cls._cache: cls._cache[key] = CharacterStyle(font, size, bold, italic, color) return cls._cache[key] # 使用functools.lru_cache作为享元 @lru_cache(maxsize=128) def get_style_flyweight(font, size, bold, italic, color): """函数级享元 -- 更Pythonic的方式""" return CharacterStyle(font, size, bold, italic, color) # 验证共享 s1 = get_style_flyweight("Arial", 12, False, False, "black") s2 = get_style_flyweight("Arial", 12, False, False, "black") print(s1 is s2) # True -- 同一个对象 print(get_style_flyweight.cache_info()) # CacheInfo(hits=0, misses=1, ...)

结构型模式总结

  • Python的鸭子类型使适配器模式几乎"隐形"
  • 函数装饰器和类装饰器是装饰器模式的终极Pythonic实现
  • __getattr__和描述符协议让代理模式实现更加简洁
  • lru_cache天然适合享元模式的缓存共享需求

四、行为型模式的函数式简化

行为型模式关注对象之间的通信和责任分配。Python的一等函数、闭包、生成器等特性,使得许多行为型模式可以用函数式的方式大幅简化。

4.1 策略模式(Strategy)

策略模式定义一系列算法并使其可互换。在Python中,策略就是一等函数 —— 不需要策略类层次结构。

# 策略模式 -- 一等函数即策略 from decimal import Decimal, ROUND_HALF_UP from dataclasses import dataclass from typing import Callable # 策略就是普通函数,不需要类 def percentage_discount(price: Decimal, total: Decimal) -> Decimal: """满100打9折""" return total * Decimal("0.9") if total >= Decimal("100") else total def fixed_amount_discount(price: Decimal, total: Decimal) -> Decimal: """满200减30""" return total - Decimal("30") if total >= Decimal("200") else total def loyalty_discount(price: Decimal, total: Decimal) -> Decimal: """会员95折""" return total * Decimal("0.95") if True else total def no_discount(price: Decimal, total: Decimal) -> Decimal: return total @dataclass class Order: items: list prices: list discount_strategy: Callable = no_discount def calculate_total(self) -> Decimal: total = sum(self.prices, Decimal("0")) return self.discount_strategy(self.prices[0], total) # 运行时更换策略 order = Order( items=["Book", "Pen"], prices=[Decimal("80"), Decimal("30")], discount_strategy=percentage_discount ) print(f"Total after discount: {order.calculate_total()}") # 99.0 # 甚至可以用lambda作为即用策略 order.discount_strategy = lambda p, t: t * Decimal("0.8") print(f"Total after special: {order.calculate_total()}") # 88.0

4.2 观察者模式(Observer)

观察者模式定义一对多依赖,当一个对象状态改变时自动通知依赖对象。Python的事件系统、回调函数和弱引用让观察者模式实现非常简洁。

# 观察者模式 -- 基于回调函数的Pythonic实现 from typing import Callable, list from weakref import ref class Observable: """基于回调的事件源 -- 比标准观察者模式更Pythonic""" def __init__(self): self._observers = [] # 存储回调函数 def register(self, callback: Callable): """注册观察者(回调)""" self._observers.append(callback) return callback # 支持装饰器用法 def unregister(self, callback): self._observers.remove(callback) def _notify(self, event, **data): """通知所有观察者""" for callback in list(self._observers): callback(event, self, **data) class WeatherStation(Observable): def __init__(self): super().__init__() self._temperature = 0 self._humidity = 0 @property def temperature(self): return self._temperature @temperature.setter def temperature(self, value): self._temperature = value self._notify("temperature_changed", new_value=value) @property def humidity(self): return self._humidity @humidity.setter def humidity(self, value): self._humidity = value self._notify("humidity_changed", new_value=value) # 观察者就是普通函数 def log_temperature(event, source, **data): print(f"[LOG] Temperature changed to {data['new_value']}C") @station.register # 装饰器注册 def alert_if_hot(event, source, **data): if event == "temperature_changed" and data["new_value"] > 35: print("[ALERT] High temperature warning!") station = WeatherStation() station.register(log_temperature) # 普通注册 station.temperature = 30 # [LOG] Temperature changed to 30C station.temperature = 36 # [LOG] + [ALERT]

4.3 命令模式(Command)

命令模式将请求封装为对象。Python中函数本身就是对象,可以作为命令传递,函数装饰器也可以实现更复杂的命令包装。

# 命令模式 -- 函数即命令 from dataclasses import dataclass from typing import Callable import datetime @dataclass class Command: """命令对象 -- 封装可调用和元数据""" action: Callable args: tuple = () kwargs: dict = None created_at: str = None def __post_init__(self): self.created_at = datetime.datetime.now().isoformat() def execute(self): return self.action(*self.args, **(self.kwargs or {})) def __repr__(self): return f"Command({self.action.__name__}, created={self.created_at})" # 命令队列 -- 支持撤销的命令 class CommandHistory: def __init__(self): self._undo_stack = [] self._redo_stack = [] def execute(self, command: Command): result = command.execute() self._undo_stack.append(command) self._redo_stack.clear() return result def undo(self): if not self._undo_stack: return cmd = self._undo_stack.pop() self._redo_stack.append(cmd) return f"Undone: {cmd}" # 具体命令就是普通函数 def add(a, b): return a + b def multiply(a, b): return a * b def greet(name): return f"Hello, {name}!" # 使用 history = CommandHistory() result = history.execute(Command(add, args=(3, 4))) print(result) # 7 result = history.execute(Command(greet, kwargs={"name": "Python"})) print(result) # Hello, Python!

4.4 模板方法模式(Template Method)

模板方法在父类中定义算法骨架,子类覆盖特定步骤。Python中可以用继承,但更灵活的方式是使用高阶函数或依赖注入。

# 模板方法 -- 传统方式 vs Pythonic方式 # 传统方式(GoF风格) -- 使用抽象基类 from abc import ABC, abstractmethod class DataProcessor(ABC): """模板方法模式 -- 骨架由父类定义""" def process(self): """模板方法 -- 定义算法骨架""" data = self._load_data() cleaned = self._clean(data) result = self._analyze(cleaned) self._output(result) return result @abstractmethod def _load_data(self): pass def _clean(self, data): return data # 钩子方法 @abstractmethod def _analyze(self, data): pass def _output(self, result): print(result) class CSVProcessor(DataProcessor): def _load_data(self): return "csv data" def _analyze(self, data): return f"analysis of {data}" # Pythonic方式 -- 使用高阶函数,无需继承 def create_processor(load_fn, analyze_fn, clean_fn=None, output_fn=print): """高阶函数:接受策略函数,返回处理器""" def process(): data = load_fn() cleaned = clean_fn(data) if clean_fn else data result = analyze_fn(cleaned) output_fn(result) return result return process # 使用函数式模板方法 processor = create_processor( load_fn=lambda: "json data", analyze_fn=lambda d: f"analysis of {d}", clean_fn=lambda d: d.strip() ) result = processor()

4.5 迭代器模式(Iterator)

迭代器模式提供顺序访问聚合对象元素的方法。Python的迭代器协议(__iter__和__next__)和生成器让这一模式完全内化。

# 迭代器模式 -- Python生成器 = 终极迭代器实现 # 方式一:实现迭代器协议 class Range: def __init__(self, start, end): self.start = start self.end = end def __iter__(self): return self def __next__(self): if self.start >= self.end: raise StopIteration current = self.start self.start += 1 return current # 方式二:生成器函数(最Pythonic) def fibonacci(limit): """生成器函数 -- 即迭代器""" a, b = 0, 1 for _ in range(limit): yield a a, b = b, a + b # 方式三:生成器表达式 squares = (x * x for x in range(10)) # 方式四:使用itertools组合迭代器 import itertools # 无限迭代器 + 切片 natural_numbers = itertools.count(1) first_ten = itertools.islice(natural_numbers, 10) print(list(first_ten)) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 自定义迭代器 -- 遍历树形结构 def walk_tree(node): """递归生成器:深度优先遍历树""" yield node for child in getattr(node, 'children', []): yield from walk_tree(child)

4.6 状态模式(State)

状态模式允许对象在其内部状态改变时改变其行为。Python的枚举和字典派发表可以简化状态机实现。

# 状态模式 -- 使用枚举和函数派发表 from enum import Enum, auto class TrafficLightState(Enum): RED = auto() GREEN = auto() YELLOW = auto() class TrafficLight: """基于函数派发表的状态机 -- 比GoF状态模式更简洁""" def __init__(self): self.state = TrafficLightState.RED # 状态转换表 self._transitions = { TrafficLightState.RED: TrafficLightState.GREEN, TrafficLightState.GREEN: TrafficLightState.YELLOW, TrafficLightState.YELLOW: TrafficLightState.RED, } def change(self): self.state = self._transitions[self.state] def action(self): """基于当前状态派发行为""" actions = { TrafficLightState.RED: lambda: "Stop", TrafficLightState.GREEN: lambda: "Go", TrafficLightState.YELLOW: lambda: "Caution", } return actions[self.state]() # 更复杂的状态机 -- 使用状态类 class OrderState: def next_state(self): pass def can_cancel(self): return False class PendingState(OrderState): def next_state(self): return PaidState() def can_cancel(self): return True class PaidState(OrderState): def next_state(self): return ShippedState() def can_cancel(self): return False class ShippedState(OrderState): def next_state(self): return DeliveredState() def can_cancel(self): return True class DeliveredState(OrderState): def next_state(self): return self class Order: def __init__(self): self.state = PendingState() def advance(self): self.state = self.state.next_state() def can_cancel(self): return self.state.can_cancel()

4.7 职责链模式(Chain of Responsibility)

职责链将请求沿处理者链传递。Python中可以用列表或生成器实现优雅的职责链。

# 职责链 -- 函数列表 + 生成器 from typing import Callable, Optional class LoggingHandler: """基于类方法的职责链""" def __init__(self): self._handlers = [] def add(self, handler: Callable): self._handlers.append(handler) return self def handle(self, level, message): """沿职责链传递,直到被处理""" for handler in self._handlers: result = handler(level, message) if result is not None: # 非None表示已处理 return result return "Unhandled" # 定义处理器函数 def error_handler(level, msg): if level == "ERROR": return f"[ERROR] {msg}" return None # 不处理,传递给下一个 def warning_handler(level, msg): if level == "WARNING": return f"[WARNING] {msg}" return None def info_handler(level, msg): if level == "INFO": return f"[INFO] {msg}" return None # 组装职责链 chain = (LoggingHandler() .add(error_handler) .add(warning_handler) .add(info_handler)) print(chain.handle("ERROR", "Disk full")) # [ERROR] Disk full print(chain.handle("INFO", "User logged in")) # [INFO] User logged in

其余行为型模式(中介者、备忘录、访问者、解释器)在日常Python开发中使用频率相对较低,但在特定场景下仍有价值。例如,中介者模式可以通过事件总线统一协调组件通信,备忘录模式可以通过__getstate__/__setstate__或pickle实现状态快照,访问者模式可以通过functools.singledispatch实现泛型函数分发。

行为型模式总结

  • 一等函数让策略、命令、观察者模式"消失"为函数调用
  • 生成器和迭代器协议完全替代了GoF的迭代器模式
  • 枚举+派发表简化了状态模式的实现
  • 函数列表和生成器组成了优雅的职责链
  • 模板方法既可以用继承,也可以用高阶函数注入

五、Python特有设计模式

Python语言本身的特性催生了一些独特的"模式",它们并非GoF的一部分,但在Python生态中广泛使用,是Pythonic代码的核心组成部分。

5.1 可调用对象(Callable Objects)

通过实现__call__,让对象像函数一样调用。这是保存状态的可调用对象(类似闭包)。

# 可调用对象 -- 有状态的函数 class Counter: """保存调用次数的可调用对象""" def __init__(self): self.count = 0 def __call__(self, *args, **kwargs): self.count += 1 return f"Called {self.count} times with {args} {kwargs}" counter = Counter() print(counter("hello")) # Called 1 times with ('hello',) {} print(counter()) # Called 2 times with () {} print(callable(counter)) # True # 偏函数 -- functools.partial from functools import partial def power(base, exp): return base ** exp square = partial(power, exp=2) cube = partial(power, exp=3) print(square(5)) # 25 print(cube(3)) # 27

5.2 上下文管理器(Context Manager)

上下文管理器通过__enter__和__exit__协议实现资源的自动管理。这是Python最著名的特有模式之一。

# 上下文管理器 -- 资源管理的标准模式 class ManagedFile: """自定义上下文管理器""" def __init__(self, path, mode='r'): self.path = path self.mode = mode def __enter__(self): self.file = open(self.path, self.mode) return self.file def __exit__(self, exc_type, exc_val, exc_tb): self.file.close() return False # 不抑制异常 # 使用contextlib简化 from contextlib import contextmanager @contextmanager def timer(name="block"): """用生成器实现的上下文管理器 -- 自动计时""" import time start = time.perf_counter() try: yield finally: elapsed = time.perf_counter() - start print(f"[{name}] took {elapsed:.4f}s") # 嵌套上下文管理器 with timer("database query"): with ManagedFile("data.txt", "r") as f: data = f.read()

5.3 描述符(Descriptor)

描述符通过__get__、__set__、__delete__协议,控制属性访问行为。property、classmethod、staticmethod都是描述符的应用。

# 描述符 -- 控制属性访问 class PositiveNumber: """描述符:验证属性为正数""" def __init__(self, default=0): self.default = default def __set_name__(self, owner, name): self._private_name = f"_{name}" def __get__(self, obj, objtype=None): if obj is None: return self return getattr(obj, self._private_name, self.default) def __set__(self, obj, value): if value <= 0: raise ValueError(f"Value must be positive, got {value}") setattr(obj, self._private_name, value) class Product: price = PositiveNumber() quantity = PositiveNumber() def __init__(self, name, price, quantity): self.name = name self.price = price # 触发描述符的__set__ self.quantity = quantity def total(self): return self.price * self.quantity # 使用 -- 自动验证 p = Product("Laptop", 999.99, 5) print(p.total()) # 4999.95 try: p.price = -100 # ValueError: Value must be positive, got -100 except ValueError as e: print(e)

5.4 元类(Metaclass)

元类是创建类的类,是Python中最强大的元编程工具。适用于框架开发、ORM、API设计等场景。

# 元类 -- 控制类的创建 class RegistryMeta(type): """元类:自动注册所有子类""" registry = {} def __new__(mcs, name, bases, namespace): cls = super().__new__(mcs, name, bases, namespace) if name != "BasePlugin": mcs.registry[name.lower()] = cls return cls class BasePlugin(metaclass=RegistryMeta): """所有插件的基础类""" class PDFPlugin(BasePlugin): def process(self, path): return f"Processing PDF: {path}" class ImagePlugin(BasePlugin): def process(self, path): return f"Processing Image: {path}" # 所有子类自动注册 print(RegistryMeta.registry) # {'pdfplugin': <class '__main__.PDFPlugin'>, 'imageplugin': <class '__main__.ImagePlugin'>} # 元类应用:单例元类 class SingletonMeta(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super().__call__(*args, **kwargs) return cls._instances[cls] class Database(metaclass=SingletonMeta): def __init__(self): self.connected = False

重要提醒

元类是Python最强大的元编程工具,但也是最容易被滥用的工具。Python之禅说"Simple is better than complex",在90%的场景下,装饰器、描述符或继承都足以解决问题。需要元类时,往往是你在开发框架或库,而非应用程序。

六、设计模式选择指南

选择合适的设计模式往往比实现模式本身更具挑战。以下指南助你在Python项目中做出正确的模式选择。

6.1 快速选择矩阵

需求场景 推荐模式 Python实现建议
需要确保全局唯一实例 单例模式 模块级实例(最优先)、装饰器、元类
对象创建逻辑复杂 工厂模式 工厂函数、字典注册表
构建复杂对象需分步骤 建造者模式 链式调用 + dataclass
需要运行时动态添加职责 装饰器模式 函数装饰器、类装饰器
算法需要运行时互换 策略模式 一等函数(lambda或函数引用)
对象状态改变时通知多个依赖 观察者模式 回调函数列表、信号库
需要顺序访问聚合对象 迭代器模式 生成器(yield/yield from)
对象行为依赖其内部状态 状态模式 枚举 + 字典派发表
为复杂子系统提供简化接口 外观模式 封装类(Facade)
控制对对象的访问 代理模式 __getattr__转发、描述符
需要对象或资源自动清理 上下文管理器 __enter__/__exit__协议
需要属性级别的验证控制 描述符 描述符协议

6.2 何时不需要设计模式

如果Python有内置方案,就不要用GoF模式:
  • 需要单例 → 用模块,不要写Singleton类
  • 需要迭代器 → 用生成器,不要实现Iterator接口
  • 需要装饰者 → 用@decorator,不要包装类
  • 需要策略 → 用函数/lambda,不要策略类层次
  • 需要工厂 → 用函数/类方法,不要工厂类
  • 需要命令 → 用函数/functools.partial,不要命令类
  • 需要观察者 → 用回调列表/信号,不要Observer接口
  • 需要资源管理 → 用上下文管理器,不要手动try/finally

6.3 选择原则

Python模式选择五原则

  1. Pythonic优先: 标准库提供了解决方案时,优先使用标准库
  2. 简单优先: 函数能解决的问题,不要引入类
  3. 组合优先: 优先使用组合而非继承
  4. 显式优于隐式: 模式的使用应该让代码意图更清晰,而非更模糊
  5. 模式是手段不是目的: 为了使用模式而重构,往往适得其反

"设计模式是你的工具箱,不是你的建筑设计蓝图。知道什么时候不使用模式,和知道什么时候使用模式同样重要。"

七、核心要点总结

八、进一步思考与实践

设计模式的学习不应止步于记忆UML类图和实现方法。真正的掌握来自于在实战中体会模式背后的设计原则。以下是一些建议:

学习路径建议

  • 阅读源码: 阅读知名Python框架源码(如Requests、Flask、Click、SQLAlchemy),分析它们使用了哪些模式
  • 重构练习: 将自己过去写的代码用模式思想重构,体会模式带来的是简化还是复杂化
  • 理解而非记忆: 重要的是理解每个模式解决了什么"问题",而非记住实现代码
  • 跨语言对比: 比较同一模式在Java/C++和Python中的实现差异,理解语言特性对模式的影响
  • 关注反模式: 识别和避免Python中的常见反模式(如滥用元类、过度设计)

实践项目推荐

  1. 用策略模式+观察者模式构建一个交易信号系统
  2. 用组合模式+访问者模式构建文件系统分析工具
  3. 用状态模式+职责链模式构建订单处理工作流
  4. 用上下文管理器+描述符构建带重试和超时控制的数据库连接池
  5. 用元类+工厂模式构建一个插件自动发现系统