装饰器模式
问题
Python 的装饰器和 GoF 装饰器模式有什么区别?
答案
GoF 装饰器模式 vs Python 装饰器
GoF 装饰器模式通过类包装给对象动态添加功能;Python 的 @decorator 语法是函数包装器,用途更广。
函数装饰器(Python 风格)
import time
import functools
from typing import Callable, TypeVar, ParamSpec
P = ParamSpec("P")
R = TypeVar("R")
def timer(func: Callable[P, R]) -> Callable[P, R]:
@functools.wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.3f}s")
return result
return wrapper
def retry(max_attempts: int = 3):
"""参数化装饰器"""
def decorator(func: Callable[P, R]) -> Callable[P, R]:
@functools.wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
time.sleep(2 ** attempt)
return wrapper
return decorator
# 装饰器叠加
@timer
@retry(max_attempts=3)
def fetch_data(url: str) -> dict:
...
类装饰器模式(GoF 风格)
from abc import ABC, abstractmethod
class DataSource(ABC):
@abstractmethod
def read(self) -> str: ...
@abstractmethod
def write(self, data: str) -> None: ...
class FileDataSource(DataSource):
def __init__(self, filename: str):
self.filename = filename
def read(self) -> str:
with open(self.filename) as f:
return f.read()
def write(self, data: str) -> None:
with open(self.filename, "w") as f:
f.write(data)
# 装饰器:添加加密功能
class EncryptionDecorator(DataSource):
def __init__(self, source: DataSource):
self._source = source
def read(self) -> str:
data = self._source.read()
return self._decrypt(data)
def write(self, data: str) -> None:
self._source.write(self._encrypt(data))
def _encrypt(self, data: str) -> str: ...
def _decrypt(self, data: str) -> str: ...
# 装饰器:添加压缩功能
class CompressionDecorator(DataSource):
def __init__(self, source: DataSource):
self._source = source
def read(self) -> str:
return self._decompress(self._source.read())
def write(self, data: str) -> None:
self._source.write(self._compress(data))
# ...
# 组合使用
source = CompressionDecorator(EncryptionDecorator(FileDataSource("data.txt")))
source.write("hello") # 先加密,再压缩,最后写文件
常见面试问题
Q1: 两种"装饰器"的本质区别?
答案:
| GoF 装饰器模式 | Python @decorator | |
|---|---|---|
| 作用对象 | 对象实例 | 函数/类 |
| 实现方式 | 接口+包装类 | 高阶函数 |
| 时机 | 运行时动态组合 | 定义时静态包装 |
| 典型场景 | I/O 流包装、组件增强 | 日志、缓存、权限检查 |
Q2: functools.wraps 有什么用?
答案:
装饰器会把原函数的 __name__、__doc__ 等元信息替换为 wrapper 的。@functools.wraps(func) 把这些信息拷贝回来,确保调试和文档正常。
Python 装饰器的详细用法参考 Python 基础 - 装饰器。