核心概念
📌 什么是装饰器?
装饰器(Decorator)是 Python 中一种强大的语法特性,它允许你在不修改原函数代码的情况下,动态地扩展函数的功能。装饰器本质上是一个接收函数作为参数并返回新函数的高阶函数。
装饰器使用 @ 符号作为语法糖,使代码更加简洁优雅。它是 Python 函数式编程和元编程的重要组成部分。
代码复用
将通用功能抽象为装饰器,避免代码重复,提高可维护性
关注点分离
将横切关注点(如日志、权限)与业务逻辑解耦
动态增强
在运行时动态修改函数行为,无需改动源代码
可组合性
多个装饰器可以叠加使用,灵活组合功能
工作原理
🔍 装饰器的本质
要理解装饰器,首先需要理解 Python 中函数是一等公民这一概念。函数可以:
- 作为参数传递给其他函数
- 作为其他函数的返回值
- 赋值给变量
- 存储在数据结构中
💡 核心理解
当你写 @decorator 放在函数定义上方时,Python
会自动将该函数作为参数传递给装饰器,并用装饰器的返回值替换原函数。这就是为什么装饰器必须返回一个可调用对象。
代码示例
🔰 示例1:基础装饰器 - 函数计时器
最基础的装饰器用法,用于测量函数执行时间:
import time
from functools import wraps
# 定义一个计时装饰器
def timer(func):
"""测量函数执行时间的装饰器"""
@wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
start_time = time.time() # 记录开始时间
result = func(*args, **kwargs) # 调用原函数
end_time = time.time() # 记录结束时间
print(f"函数 {func.__name__} 执行耗时: {end_time - start_time:.4f} 秒")
return result
return wrapper
# 使用装饰器
@timer
def slow_function():
"""模拟一个耗时操作"""
time.sleep(1)
return "完成!"
# 调用函数
result = slow_function()
print(f"返回值: {result}")
⚡ 示例2:带参数的装饰器 - 重试机制
当装饰器本身需要接收参数时,需要再嵌套一层函数:
import time
from functools import wraps
# 带参数的重试装饰器
def retry(max_attempts=3, delay=1):
"""
自动重试装饰器
:param max_attempts: 最大重试次数
:param delay: 重试间隔(秒)
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
print(f"第 {attempts} 次尝试失败: {e}")
if attempts < max_attempts:
print(f"等待 {delay} 秒后重试...")
time.sleep(delay)
raise Exception(f"函数 {func.__name__} 在 {max_attempts} 次尝试后仍然失败")
return wrapper
return decorator
# 模拟不稳定的网络请求
attempt_count = 0
@retry(max_attempts=3, delay=0.5)
def unstable_network_call():
"""模拟不稳定的网络请求"""
global attempt_count
attempt_count += 1
if attempt_count < 3:
raise ConnectionError("网络连接失败")
return "请求成功!"
# 测试重试机制
result = unstable_network_call()
print(f"最终结果: {result}")
🚀 示例3:类装饰器 - 缓存机制
使用类实现装饰器,可以维护状态,实现更复杂的功能:
from functools import wraps
class Memoize:
"""
缓存装饰器类
缓存函数的计算结果,避免重复计算
"""
def __init__(self, func):
self.func = func
self.cache = {} # 存储缓存结果
wraps(func)(self) # 保留原函数元信息
def __call__(self, *args):
# 检查缓存中是否已有结果
if args in self.cache:
print(f" → 缓存命中: {args}")
return self.cache[args]
# 计算结果并存入缓存
print(f" → 计算中: {args}")
result = self.func(*args)
self.cache[args] = result
return result
def clear_cache(self):
"""清空缓存"""
self.cache.clear()
# 使用缓存装饰器优化递归计算
@Memoize
def fibonacci(n):
"""计算斐波那契数列第 n 项"""
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# 测试缓存效果
print("计算 fibonacci(5):")
result = fibonacci(5)
print(f"结果: {result}\n")
print("再次计算 fibonacci(5):")
result = fibonacci(5)
print(f"结果: {result}")
高级应用
🎯 常见应用场景
日志记录
自动记录函数调用参数、返回值和执行时间
权限验证
在执行函数前检查用户权限,实现访问控制
结果缓存
缓存函数计算结果,避免重复计算提升性能
参数验证
在函数执行前自动验证参数类型和范围
事务管理
数据库事务的自动提交和回滚处理
限流控制
限制函数的调用频率,防止资源过度使用
最佳实践
✅ 使用要点
使用 @wraps
始终使用 functools.wraps 保留原函数的元信息(__name__, __doc__ 等)
支持所有参数
使用 *args 和 **kwargs 确保装饰器能处理任意参数的函数
保持简单
每个装饰器只做一件事,遵循单一职责原则
添加文档
为装饰器编写清晰的文档字符串说明其功能
⚠️ 常见陷阱
- 忘记调用原函数或返回其结果
- 带参数装饰器忘记额外的嵌套层
- 装饰器副作用影响了函数的纯净性
- 过度使用装饰器导致调试困难
💡 Python 标准库中的装饰器
• @staticmethod - 定义静态方法
• @classmethod - 定义类方法
• @property - 将方法转为属性访问
• @functools.lru_cache - 内置的缓存装饰器
• @dataclasses.dataclass - 自动生成类方法