Python 装饰器高级用法与实战应用
装饰器(Decorator)是 Python 中最强大的特性之一,它允许我们在不修改原有函数代码的情况下,动态地扩展函数的功能。本文将深入探讨装饰器的高级用法,并通过实际代码示例展示其在项目中的应用场景。
装饰器基础回顾
装饰器本质上是接受一个函数作为参数,并返回一个新函数的高阶函数。最简单的装饰器形式如下:
def simple_decorator(func):
def wrapper():
print("执行前")
result = func()
print("执行后")
return result
return wrapper
@simple_decorator
def greet():
return "Hello, World!"
greet() # 输出:执行前 -> Hello, World! -> 执行后
带参数的装饰器
实际应用中,我们经常需要创建可配置的装饰器。这需要使用装饰器工厂模式:
def repeat(times=1):
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
results.append(func(*args, **kwargs))
return results if len(results) > 1 else results[0]
return wrapper
return decorator
@repeat(times=3)
def say_hello(name):
return f"Hello, {name}!"
print(say_hello("Alice")) # ['Hello, Alice!', 'Hello, Alice!', 'Hello, Alice!']
保留函数元信息
使用装饰器时,原始函数的元信息会被 wrapper 函数覆盖。Python 提供了 functools.wraps 装饰器来解决这个问题:
from functools import wraps
import time
def timing(func):
@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 wrapper
@timing
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))
类装饰器
装饰器不仅可以是函数,也可以是类。类装饰器通过 __call__ 方法实现:
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
functools.update_wrapper(self, func)
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} 已被调用 {self.count} 次")
return self.func(*args, **kwargs)
@CountCalls
def process_data(data):
return f"处理数据: {data}"
process_data([1, 2, 3])
装饰器堆叠
多个装饰器可以叠加使用,执行顺序从内到外(从下到上):
def uppercase(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper() if isinstance(result, str) else result
return wrapper
def exclaim(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return f"{result}!" if isinstance(result, str) else result
return wrapper
@uppercase
@exclaim
def greet(name):
return f"hello, {name}"
print(greet("Bob")) # 输出:HELLO, BOB!
带状态的装饰器
装饰器可以维护内部状态,在多次调用间共享数据。下面实现一个带大小限制的缓存装饰器:
def cache(max_size=128):
cache_dict = {}
access_order = []
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
key = (args, frozenset(kwargs.items()))
if key in cache_dict:
access_order.remove(key)
access_order.append(key)
return cache_dict[key]
result = func(*args, **kwargs)
if len(cache_dict) >= max_size and key not in cache_dict:
oldest_key = access_order.pop(0)
del cache_dict[oldest_key]
cache_dict[key] = result
access_order.append(key)
return result
wrapper.clear_cache = lambda: (cache_dict.clear(), access_order.clear())
return wrapper
return decorator
@cache(max_size=3)
def expensive_computation(x):
print(f"执行复杂计算: {x}")
return x ** 2
print(expensive_computation(5))
print(expensive_computation(5)) # 缓存命中
参数验证装饰器
装饰器非常适合用于函数参数验证:
import inspect
def validate_types(**type_checks):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
sig = inspect.signature(func)
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
for param_name, expected_type in type_checks.items():
if param_name in bound_args.arguments:
value = bound_args.arguments[param_name]
if not isinstance(value, expected_type):
raise TypeError(f"参数 '{param_name}' 类型错误")
return func(*args, **kwargs)
return wrapper
return decorator
@validate_types(name=str, age=int, scores=list)
def register_student(name, age, scores):
return f"注册学生: {name}, {age}岁, 成绩: {scores}"
print(register_student("张三", 18, [90, 85, 92]))
异常处理装饰器
统一的异常处理可以通过装饰器实现:
def handle_exceptions(default_return=None, log_errors=True):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except ValueError as e:
if log_errors:
print(f"值错误: {e}")
return default_return
except Exception as e:
if log_errors:
print(f"未知错误: {e}")
return default_return
return wrapper
return decorator
@handle_exceptions(default_return=None)
def parse_int(value):
return int(value)
print(parse_int("123")) # 123
print(parse_int("abc")) # None
重试机制装饰器
在调用外部 API 或不稳定服务时,重试机制非常有用:
def retry(max_attempts=3, delay=1, backoff=2):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
current_delay = delay
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
if attempts == max_attempts:
raise
print(f"尝试 {attempts}/{max_attempts} 失败")
time.sleep(current_delay)
current_delay *= backoff
return wrapper
return decorator
实战应用:API 请求限流
下面实现一个简单的限流器装饰器,控制 API 请求的频率:
class RateLimiter:
def __init__(self, max_calls=10, time_window=60):
self.max_calls = max_calls
self.time_window = time_window
self.calls = []
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
now = time.time()
self.calls = [t for t in self.calls if now - t < self.time_window]
if len(self.calls) >= self.max_calls:
wait_time = self.time_window - (now - self.calls[0])
raise Exception(f"超过限流,请等待 {wait_time:.1f} 秒")
self.calls.append(now)
return func(*args, **kwargs)
return wrapper
@RateLimiter(max_calls=3, time_window=10)
def api_request(endpoint):
return f"请求 {endpoint} 成功"
总结
Python 装饰器是一个强大而灵活的工具,通过本文的学习,你应该掌握了:
- 基础装饰器 - 理解装饰器的核心概念
- 带参数装饰器 - 创建可配置的装饰器
- 类装饰器 - 使用类实现更复杂的装饰逻辑
- 装饰器堆叠 - 组合多个装饰器
- 带状态装饰器 - 维护调用间的状态
- 实用场景 - 缓存、限流、重试、验证等
在实际项目中,合理使用装饰器可以大大提高代码的可读性和可维护性,减少重复代码,实现横切关注点的优雅解耦。装饰器的核心思想是"不修改原代码,动态扩展功能",这正是 Pythonic 风格的体现。
