Python 装饰器实战:从原理到高级应用
在 Python 开发中,我们经常需要在函数执行前后添加额外的逻辑,比如日志记录、性能计时、权限验证等。如果直接修改函数内部代码,会导致代码重复和耦合。装饰器正是为了解决这个问题而生。
什么是装饰器?
装饰器本质上是一个高阶函数,它接受一个函数作为参数,并返回一个新的函数。语法糖 @ 让装饰器的使用变得优雅简洁。让我们从最简单的例子开始:
基础装饰器示例
假设我们想要在函数执行时打印一条日志:
def log_execution(func):
def wrapper(*args, **kwargs):
print(f"正在执行函数: {func.__name__}")
result = func(*args, **kwargs)
print(f"函数 {func.__name__} 执行完成")
return result
return wrapper
@log_execution
def calculate_sum(numbers):
return sum(numbers)
# 调用函数
resultresult = calculate_sum([1, 2, 3, 4, 5])
print(f"计算结果: {result}")
装饰器工作原理
当我们使用 @log_execution 时,Python 实际上执行了:calculate_sum = log_execution(calculate_sum)。原本的 calculate_sum 函数被替换为了 wrapper 函数,wrapper 函数会在调用原始函数前后执行额外逻辑。
带参数的装饰器
有时候我们需要让装饰器接受参数,这就需要再包一层函数:
def repeat_execution(times):
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for i in range(times):
print(f"第 {i 1} 次执行")
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat_execution(times=3)
def process_data(data):
return [x * 2 for x in data]
# 调用将执行 3 次
resultsresults = process_data([1, 2, 3])
实战场景 1:性能计时装饰器
在性能优化时,我们需要矩道函数的执行时间。用装饰器可以轻松实现:
import time
from functools import wraps
def timing(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time Time.time()
result = func(*args, **kwargs)
end_time Time.time()
execution_time = end_time - start_time
print(f"函数 {func.__name__} 执行时间: {execution_time:.4f} 秒")
return result
return wrapper
@timing
def slow_operation():
time.sleep(1.5)
return "操作完成"
slow_operation()
为什么要使用 functools.wraps?
装饰器会替换原始函数,导致函数的元信息(如 __name__、__doc__)丢失。使用 @wraps 可以保留这些信息:
from functools import wraps
def better_decorator(func):
@wraps(func) # 保留原始函数的元信息
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@better_decorator
def my_function():
"""这是一个重要的函数"""
pass
print(my_function.__name__) # 输出: my_function
print(my_function.__doc__) # 输出: 这是一个重要的函数
实战场景 2:缓存装饰器
对于耗时且参数相同的计算,我们可以用缓存避免重复计算:
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) fibonacci(n-2)
# 第一次调用会计算
print(fibonacci(50))
# 第二次调用直接从缓存读取
print(fibonacci(50))
自定义缓存装饰器
我们也可以自己实现一个简单的缓存装饰器:
def simple_cache(func):
cache = {}
def wrapper(*args):
if args in cache:
print(f"从缓存读取: {args}")
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@simple_cache
def expensive_calculation(x):
print(f"正在计算: {x}")
return x ** 2
expensive_calculation(10) # 计算并缓存
expensive_calculation(10) # 从缓存读取
实战场景 3:权限验证装饰器
在 Web 开发中,装饰器常用于权限验证:
def require_admin(func):
def wrapper(user, *args, **kwargs):
if not user.get(is_admin, False):
raise PermissionError("需要管理员权限")
return func(user, *args, **kwargs)
return wrapper
@require_admin
def delete_user(admin_user, user_id):
print(f"删除用户: {user_id}")
return "删除成功"
admin = {name: admin, is_admin: True}
delete_user(admin, 123)
实战场景 4:重试装饰器
对于可能失败的网络请求,我们可以用装饰器实现自动重试:
import random
def retry(max_attempts=3, delay=1):
def decorator(func):
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
print(f"第 {attempt 1} 次尝试失败,准备重试...")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3)
def unstable_api_call():
# 模拟有 70% 概率失败
if random.random() < 0.7:
raise ConnectionError("连接失败")
return "请求成功"
unstable_api_call()
类装饰器
除了函数装饰器,Python 还支持类装饰器。类装饰器是一个实现了 __call__ 方法的类:
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count = 1
print(f"函数已调用 {self.count} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
装饰器最佳实践
1. 始终使用 functools.wraps 保留函数元信息
2. 装饰器应该专注于单一职责,不要做太多事情
3. 在文档字符串中说明装饰器的行为
4. 考虑装饰器的执行顺序,多个装饰器会从下到上udi 执行
多个装饰器的组合
@timing
@log_execution
def complex_task():
time.sleep(0.5)
return "任务完成"
# 执行顺序:log_execution -> timing -> complex_task
complex_task()
总结
装饰器是 Python 中优雅而强大的工具,它可以帮助我们:
- 分离关注点,避免代码重复
- 提高代码的可读性和可维护性
- 在不修改%E原始函数的情况下增强功能
掌握装饰器将让你的 Python 代码更加简洁、优雅和专业。
