第78集:带参数装饰器
学习目标
- 理解带参数装饰器的概念
- 掌握带参数装饰器的语法
- 学会创建带参数的装饰器
- 理解带参数装饰器的执行流程
- 掌握多层嵌套的装饰器
- 了解带参数装饰器的应用场景
什么是带参数装饰器
带参数装饰器是指装饰器本身可以接受参数的装饰器。这种装饰器需要三层嵌套函数:最外层接受装饰器参数,中间层接受被装饰函数,最内层是包装函数。
带参数装饰器的结构
def decorator_with_args(arg1, arg2):
def decorator(func):
def wrapper(*args, **kwargs):
# 使用arg1, arg2
return func(*args, **kwargs)
return wrapper
return decorator基本语法
语法结构
@decorator_with_args(arg1, arg2)
def function():
pass等价形式
def function():
pass
function = decorator_with_args(arg1, arg2)(function)基本示例
示例1:简单带参数装饰器
def repeat(times):
"""重复执行装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello()
# 输出3次Hello!示例2:带参数的日志装饰器
def log(prefix=""):
"""带前缀的日志装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
print(f"{prefix} 调用函数: {func.__name__}")
result = func(*args, **kwargs)
print(f"{prefix} 返回值: {result}")
return result
return wrapper
return decorator
@log("DEBUG:")
def add(a, b):
return a + b
add(3, 5)示例3:带参数的权限装饰器
def require_permission(permission):
"""权限验证装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
if not has_permission(permission):
raise PermissionError(f"需要{permission}权限")
return func(*args, **kwargs)
return wrapper
return decorator
@require_permission("admin")
def delete_user(user_id):
print(f"删除用户: {user_id}")
def has_permission(permission):
return permission == "admin"
delete_user(123)执行流程
执行流程详解
def my_decorator(arg1, arg2):
print(f"1. 装饰器参数: arg1={arg1}, arg2={arg2}")
def decorator(func):
print(f"2. 被装饰函数: {func.__name__}")
def wrapper(*args, **kwargs):
print(f"4. wrapper被调用")
print(f"5. 装饰器参数: arg1={arg1}, arg2={arg2}")
result = func(*args, **kwargs)
print(f"7. 函数执行完成")
return result
print(f"3. wrapper被创建")
return wrapper
return decorator
@my_decorator("hello", "world")
def my_function():
print(f"6. 原函数执行")
print("装饰器应用完成")
my_function()执行顺序
- 调用
my_decorator("hello", "world"),返回decorator函数 - 调用
decorator(my_function),返回wrapper函数 - 将
wrapper赋值给my_function - 调用
my_function()时,实际调用的是wrapper()
常用带参数装饰器模式
模式1:重复执行
def repeat(times=1):
"""重复执行函数"""
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello()模式2:超时控制
import time
from functools import wraps
def timeout(seconds):
"""超时控制装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start_time
if elapsed > seconds:
print(f"警告: {func.__name__} 执行时间 {elapsed:.2f}秒 超过 {seconds}秒")
return result
return wrapper
return decorator
@timeout(2)
def slow_function():
time.sleep(1.5)
print("函数执行完成")
slow_function()模式3:重试机制
def retry(max_attempts=3, delay=1):
"""重试装饰器"""
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
if attempts >= max_attempts:
raise
print(f"重试 {attempts}/{max_attempts}: {e}")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3, delay=1)
def unreliable_function():
import random
if random.random() < 0.7:
raise ValueError("随机错误")
return "成功"
unreliable_function()模式4:缓存控制
def cache(max_size=128):
"""缓存装饰器"""
def decorator(func):
cache_dict = {}
@wraps(func)
def wrapper(*args, **kwargs):
key = (args, frozenset(kwargs.items()))
if key in cache_dict:
print("从缓存获取")
return cache_dict[key]
if len(cache_dict) >= max_size:
cache_dict.popitem()
result = func(*args, **kwargs)
cache_dict[key] = result
print("计算并缓存")
return result
return wrapper
return decorator
@cache(max_size=3)
def expensive_function(x):
print(f"计算 {x}")
return x ** 2
expensive_function(5)
expensive_function(5)多参数装饰器
示例4:多个参数
def log(level="INFO", prefix=""):
"""多参数日志装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[{level}] {prefix} 调用 {func.__name__}")
result = func(*args, **kwargs)
print(f"[{level}] {prefix} 返回 {result}")
return result
return wrapper
return decorator
@log(level="DEBUG", prefix="API:")
def add(a, b):
return a + b
add(3, 5)示例5:可变参数装饰器
def log(*args, **kwargs):
"""可变参数日志装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*func_args, **func_kwargs):
print(f"装饰器参数: args={args}, kwargs={kwargs}")
print(f"函数参数: args={func_args}, kwargs={func_kwargs}")
result = func(*func_args, **func_kwargs)
return result
return wrapper
return decorator
@log("DEBUG", prefix="API:")
def add(a, b):
return a + b
add(3, 5)带参数装饰器的应用
应用1:性能监控
def performance_monitor(threshold=1.0):
"""性能监控装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
if elapsed > threshold:
print(f"警告: {func.__name__} 执行时间 {elapsed:.2f}秒 超过阈值 {threshold}秒")
else:
print(f"{func.__name__} 执行时间 {elapsed:.4f}秒")
return result
return wrapper
return decorator
@performance_monitor(threshold=0.5)
def fast_function():
time.sleep(0.1)
@performance_monitor(threshold=0.5)
def slow_function():
time.sleep(1.0)
fast_function()
slow_function()应用2:输入验证
def validate_types(**type_hints):
"""类型验证装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 获取函数参数名
import inspect
sig = inspect.signature(func)
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
# 验证参数类型
for param_name, param_value in bound_args.arguments.items():
if param_name in type_hints:
expected_type = type_hints[param_name]
if not isinstance(param_value, expected_type):
raise TypeError(
f"参数 {param_name} 应为 {expected_type.__name__} 类型"
)
return func(*args, **kwargs)
return wrapper
return decorator
@validate_types(a=int, b=int)
def add(a, b):
return a + b
add(3, 5) # 正常
# add(3.5, 5) # TypeError应用3:输出格式化
def format_output(formatter=str):
"""输出格式化装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return formatter(result)
return wrapper
return decorator
@format_output(formatter=lambda x: f"结果: {x:.2f}")
def calculate(a, b):
return a / b
print(calculate(10, 3))带参数装饰器的限制
限制1:参数必须在装饰时确定
# 参数必须在装饰时确定
@repeat(3) # 3是固定值
def say_hello():
print("Hello!")
# 不能在运行时动态改变参数限制2:嵌套层次多,理解困难
# 三层嵌套可能难以理解
def outer(arg1):
def middle(arg2):
def inner(func):
def wrapper(*args, **kwargs):
pass
return wrapper
return inner
return middle最佳实践
实践1:使用functools.wraps
from functools import wraps
def my_decorator(arg1, arg2):
"""带参数装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator实践2:提供默认参数
def repeat(times=1):
"""提供默认参数"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat() # 使用默认参数
def say_hello():
print("Hello!")实践3:清晰的参数命名
def retry(max_attempts=3, delay=1, backoff=2):
"""清晰的参数命名"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
pass
return wrapper
return decorator实践4:文档化装饰器
def retry(max_attempts=3, delay=1, backoff=2):
"""
重试装饰器
Args:
max_attempts: 最大重试次数
delay: 初始延迟时间(秒)
backoff: 延迟倍增因子
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
pass
return wrapper
return decorator常见错误
错误1:忘记返回装饰器函数
# 错误:忘记返回装饰器函数
def bad_decorator(arg):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
# 忘记return decorator
# 正确:返回装饰器函数
def good_decorator(arg):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator错误2:忘记使用functools.wraps
# 错误:忘记使用wraps
def bad_decorator(arg):
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
# 正确:使用wraps
from functools import wraps
def good_decorator(arg):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator错误3:参数名冲突
# 错误:参数名冲突
def bad_decorator(func):
def wrapper(func, *args, **kwargs): # func参数名冲突
return func(*args, **kwargs)
return wrapper
# 正确:使用不同的参数名
def good_decorator(original_func):
def wrapper(*args, **kwargs):
return original_func(*args, **kwargs)
return wrapper总结
带参数装饰器是Python中强大的工具,具有以下特点:
核心概念
- 带参数装饰器需要三层嵌套函数
- 最外层接受装饰器参数
- 中间层接受被装饰函数
- 最内层是包装函数
优势
- 灵活性高
- 可配置性强
- 代码复用
- 功能强大
应用场景
- 重复执行
- 超时控制
- 重试机制
- 缓存控制
- 性能监控
- 输入验证
掌握带参数装饰器将帮助你编写更灵活、更强大的Python代码。带参数装饰器是Python中重要的概念,理解它的工作原理对于编写高质量的Python程序至关重要。