Python 常用装饰器及应用场景
Python 装饰器(Decorator)是一种强大的设计模式,它允许你在不修改原有函数或类代码的前提下,动态地为其添加新功能。装饰器本质上是一个高阶函数(接收函数作为参数并返回新函数),通过 @ 语法糖简化调用。
以下是 Python 中常用的装饰器及其典型应用场景,分为内置装饰器和自定义常用装饰器两类:
一、Python 内置装饰器
这些是 Python 语言本身提供的装饰器,主要用于类和方法的定义。
| 装饰器 | 作用 | 应用场景 |
|---|---|---|
@property | 将方法转换为只读属性,支持 getter/setter/deleter | 封装私有变量,实现数据验证、计算属性(如根据身高体重自动计算 BMI) |
@classmethod | 将方法绑定到类而非实例,第一个参数为 cls | 实现备选构造函数(如从字符串解析创建对象)、操作类变量 |
@staticmethod | 定义静态方法,不需要 self 或 cls 参数 | 工具函数,逻辑上属于类但不需要访问类或实例状态 |
@dataclass (Py3.7+) | 自动生成 __init__, __repr__, __eq__ 等方法 | 快速定义数据容器类,减少样板代码 |
@abstractmethod | 定义抽象方法,强制子类实现 | 构建接口规范,用于抽象基类(ABC) |
# 示例:@property 实现数据验证
class Person:
def __init__(self, age):
self._age = None
self.age = age # 触发 setter
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("年龄不能为负数")
self._age = value二、自定义常用装饰器及应用场景
在实际开发中,我们常编写自定义装饰器来处理横切关注点(Cross-Cutting Concerns)。
1. 日志记录 (@log)
- 场景:记录函数的调用时间、参数、返回值或异常,便于调试和审计。
- 代码示例:
import functools
import logging
def log_calls(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
logging.info(f"调用 {func.__name__}, 参数: {args}, {kwargs}")
try:
result = func(*args, **kwargs)
logging.info(f"{func.__name__} 返回: {result}")
return result
except Exception as e:
logging.error(f"{func.__name__} 抛出异常: {e}")
raise
return wrapper
@log_calls
def add(a, b):
return a + b2. 性能计时 (@timer)
- 场景:测量函数执行时间,用于性能分析和优化。
- 代码示例:
import time
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} 执行耗时: {end - start:.6f} 秒")
return result
return wrapper3. 重试机制 (@retry)
- 场景:处理网络请求、数据库连接等不稳定操作,失败时自动重试。
- 代码示例:
import time
import functools
def retry(times=3, delay=1):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for i in range(times):
try:
return func(*args, **kwargs)
except Exception as e:
if i == times - 1:
raise
print(f"重试 {i+1}/{times}: {e}")
time.sleep(delay)
return wrapper
return decorator
@retry(times=3, delay=2)
def fetch_data():
# 模拟不稳定操作
raise ConnectionError("网络波动")4. 缓存结果 (@cache / @lru_cache)
- 场景:对于计算密集型或 IO 密集型且输入输出确定的函数,避免重复计算。
- 注意:Python 3.9+ 提供了内置的
@cache,旧版本可用functools.lru_cache。 - 代码示例:
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)5. 权限校验 (@login_required / @permission)
- 场景:Web 开发中(如 Flask/Django),检查用户是否登录或拥有特定权限。
- 代码示例(简化版):
def login_required(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
current_user = get_current_user() # 假设获取当前用户的函数
if not current_user.is_authenticated:
raise PermissionError("未登录")
return func(*args, **kwargs)
return wrapper6. 参数验证 (@validate)
- 场景:确保传入函数的参数符合特定条件(如类型、范围)。
- 代码示例:
def validate_positive(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for arg in args:
if isinstance(arg, (int, float)) and arg <= 0:
raise ValueError("参数必须为正数")
return func(*args, **kwargs)
return wrapper三、编写装饰器的最佳实践
- 使用 functools.wraps:务必在内部 wrapper 函数上使用 @functools.wraps(func)。这会保留原函数的元数据(如 __name__, __doc__),否则调试和文档生成工具会显示 wrapper 的信息而不是原函数信息。
- 支持任意参数:定义 wrapper 时使用 *args 和 **kwargs,确保装饰器可以应用于任何签名的函数。
- 带参数的装饰器:如果装饰器本身需要参数(如 @retry(times=3)),需要三层嵌套函数(外层接收装饰器参数,中间层接收函数,内层执行逻辑)。
- 类装饰器:除了函数装饰器,还可以定义类装饰器(实现 __call__ 方法的类),用于更复杂的状态管理。
总结
装饰器是 Python“优雅”和“简洁”哲学的体现。通过合理使用装饰器,可以将业务逻辑与通用功能(如日志、监控、安全)分离,使代码更易维护、复用和测试。