Python 装饰器的 5 个实用场景:从入门到精通
装饰器(Decorator)是 Python 中的"函数包装器",它允许我们在不修改原函数代码的前提下,动态地添加功能。很多初学者学完 @decorator 语法后就止步不前,但实际上装饰器在实际工程中的应用远不止简单的日志打印。今天,我将分享 5 个我在实际项目中经常使用的装饰器模式,每个都能解决一类具体问题。
场景一:性能计时装饰器
在优化代码性能时,我们首先需要知道哪些函数是瓶颈。这个装饰器可以自动记录函数的执行时间:
import time
import functools
from typing import Callable, Any
def timer(func: Callable) -> Callable:
"""记录函数执行时间的装饰器"""
@functools.wraps(func)
def wrapper(*args, **kwargs) -> Any:
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
elapsed = end_time - start_time
print(f"⏱️ {func.__name__} 执行时间: {elapsed:.4f} 秒")
return result
return wrapper这个装饰器的关键在于使用 time.perf_counter()而不是 time.time()而是前者提供更高精度的性能计时。同时,@functools.wraps保留了原函数的元数据,这对调试和文档生成非常重要。
场景二:智能缓存装饰器
对于计算密集型且结果可复用的函数,缓存可以带来显著的性能提升。下面是一个支持过期时间的缓存装饰器:
import functools
from typing import Callable, Any, Dict
from datetime import datetime, timedelta
def cached(timeout_seconds: int = 300):
"""带过期时间的缓存装饰器"""
cache: Dict[str, tuple] = {}
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args, **kwargs) -> Any:
key = f"{func.__name__}:{args}:{sorted(kwargs.items())}"
if key in cache:
result, timestamp = cache[key]
if datetime.now() - timestamp < timedelta(seconds=timeout_seconds):
return result
result = func(*args, **kwargs)
cache[key] = (result, datetime.now())
return result
return wrapper
return decorator这个装饰器的亮点在于支持缓存过期时间,避免了无限期缓存导致的内存问题。
场景三:权限验证装饰器
在 Web 应用或 API 服务中,权限验证是常见需求。这个装饰器可以统一处理权限检查逻辑:
from dataclasses import dataclass
from typing import Callable, Any, List, Optional
@dataclass
class User:
username: str
roles: List[str]
is_active: bool = True
class PermissionDenied(Exception):
pass
def require_auth(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(user: Optional[User] = None, *args, **kwargs) -> Any:
if user is None or not user.is_active:
raise PermissionDenied("用户未登录")
return func(user, *args, **kwargs)
return wrapper
def require_roles(*allowed_roles: str):
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(user: User, *args, **kwargs) -> Any:
if not any(role in user.roles for role in allowed_roles):
raise PermissionDenied("权限不足")
return func(user, *args, **kwargs)
return wrapper
return decorator这个装饰器采用了装饰器工厂模式,可以灵活组合多个权限要求。
场景四:自动重试装饰器
网络请求、数据库操作等容易失败的场景,自动重试可以显著提高系统的健壮性:
import functools
import time
import random
from typing import Callable, Any, Tuple, Type
def retry(
max_attempts: int = 3,
delay: float = 1.0,
backoff: float = 2.0,
exceptions: Tuple[Type[Exception], ...] = (Exception,)
):
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args, **kwargs) -> Any:
current_delay = delay
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except exceptions as e:
if attempt < max_attempts:
time.sleep(current_delay)
current_delay *= backoff
raise e
return wrapper
return decorator这个重试装饰器实现了指数退避算法,每次重试的等待时间会成倍增长。
场景五:结构化日志装饰器
在生产环境中,结构化的日志对于问题排查至关重要。这个装饰器可以自动记录函数的输入输出:
import functools
import logging
from typing import Callable, Any, Optional
logger = logging.getLogger(__name__)
def logged(
level: int = logging.INFO,
mask_fields: Optional[list] = None
):
mask_fields = mask_fields or ["password", "token"]
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args, **kwargs) -> Any:
logger.info(f"调用 {func.__name__}")
try:
result = func(*args, **kwargs)
logger.info(f"完成 {func.__name__}")
return result
except Exception as e:
logger.error(f"异常 {func.__name__}: {e}")
raise
return wrapper
return decorator这个日志装饰器的特色在于支持敏感字段脱敏,避免密码等敏感信息泄露。
总结
通过以上 5 个场景,我们可以看到装饰器的强大之处:单一职责、保留元数据、参数化支持、异常处理和可测试性。装饰器是 Python 优雅性的体现之一,掌握这些模式后,你的代码将变得更加模块化和可维护。
