跳到主要内容

装饰器模式

问题

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 基础 - 装饰器

相关链接