Python 带参数装饰器
带参数装饰器通过嵌套函数实现,外层接收参数,内层执行装饰逻辑。
基本结构
带参数装饰器需要三层嵌套:
- 最外层:接收装饰器参数,返回装饰器
- 中间层:接收被装饰函数,返回包装函数
- 最内层:执行实际包装逻辑
Python
def repeat(times):
"装饰器工厂:指定重复执行次数"
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def say_hello(name):
print(f"Hello, {name}!")
say_hello("World")
# 输出:
# Hello, World!
# Hello, World!
# Hello, World!
参数传递方式
位置参数与关键字参数
Python
def log(level="INFO", prefix=""):
"支持关键字参数的日志装饰器"
def decorator(func):
def wrapper(*args, **kwargs):
print(f"[{level}] {prefix}{func.__name__} called")
return func(*args, **kwargs)
return wrapper
return decorator
@log(level="DEBUG", prefix=">>> ")
def process(data):
return data * 2
process(10) # [DEBUG] >>> process called
混合参数形式
Python
def validate(*validators, error_msg="Validation failed"):
"接收多个验证器参数"
def decorator(func):
def wrapper(*args, **kwargs):
for validator in validators:
if not validator(*args, **kwargs):
raise ValueError(error_msg)
return func(*args, **kwargs)
return wrapper
return decorator
def is_positive(x):
return x > 0
def is_even(x):
return x % 2 == 0
@validate(is_positive, is_even, error_msg="必须是正偶数")
def compute(x):
return x ** 2
compute(4) # 16
compute(-2) # ValueError: 必须是正偶数
使用 functools.wraps
带参数装饰器同样需要保留函数元信息:
Python
from functools import wraps
def retry(max_attempts=3, delay=1):
"重试装饰器"
def decorator(func):
@wraps(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 {attempt + 1} failed, retrying...")
return wrapper
return decorator
@retry(max_attempts=2)
def risky_operation():
import random
if random.random() < 0.5:
raise ValueError("Random failure")
return "Success"
类方式实现
使用类实现带参数装饰器更加清晰:
Python
class RateLimit:
"限流装饰器"
def __init__(self, calls=10, period=60):
self.calls = calls
self.period = period
self.timestamps = []
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
import time
now = time.time()
# 清理过期时间戳
self.timestamps = [t for t in self.timestamps if now - t < self.period]
if len(self.timestamps) >= self.calls:
raise RuntimeError("Rate limit exceeded")
self.timestamps.append(now)
return func(*args, **kwargs)
return wrapper
@RateLimit(calls=5, period=60)
def api_call(endpoint):
return f"Calling {endpoint}"
要点总结
| 特性 | 说明 |
|---|---|
| 结构 | 三层嵌套:工厂函数 → 装饰器 → 包装函数 |
| 参数 | 支持位置参数、关键字参数、可变参数 |
| 元信息 | 使用 @wraps(func) 保留被装饰函数属性 |
| 类实现 | __init__ 接收参数,__call__ 返回包装函数 |
带参数装饰器的核心是返回一个真正的装饰器,装饰器再返回包装函数。
D:\git2\jwdev\articles\PYTHON\进阶\装饰器深入\带参数装饰器.md
📝 发现内容有误?点击此处直接编辑