「decorator系列II」如何使用带参数的装饰器?

「decorator系列II」如何使用带参数的装饰器?
最新回答
橘猫

2021-04-10 12:58:55

带参数的装饰器本质上是一个返回装饰器的函数,它允许你在装饰函数时动态配置装饰器的行为。以下是具体使用方法和示例:

1. 基本结构

带参数的装饰器需要三层嵌套函数

  • 最外层函数:接收装饰器参数(如日志格式、权限级别等)。
  • 中间层函数:接收被装饰的函数(即传统装饰器的 func)。
  • 最内层函数:实现包装逻辑(即传统装饰器的 wrapper)。
import functoolsdef log_msg_func(msg): # 最外层:接收参数 def decorator(func): # 中间层:接收被装饰函数 @functools.wraps(func) # 保留元数据 def wrapper(*args, kwargs): # 最内层:包装逻辑 print(msg.format(name=func.__name__)) return func(*args, kwargs) return wrapper return decorator2. 使用方法

通过 @ 语法直接传递参数:

@log_msg_func("Custom log: {name}") # 传递参数def multiply(x, y): """Multiply two numbers.""" return x * ymultiply(2, 3)# 输出: Custom log: multiply# 返回: 63. 关键点说明
  • 参数传递:@log_msg_func("...") 会先调用最外层函数,返回 decorator,再将其应用到 multiply。
  • 元数据保留:使用 functools.wraps 确保被装饰函数的 __name__、__doc__ 等属性不丢失。
  • 等价形式:以下两种写法完全等价:@log_msg_func("Custom log: {name}")def multiply(x, y): ...# 等价于:def multiply(x, y): ...multiply = log_msg_func("Custom log: {name}")(multiply)
4. 实际应用场景
  • 动态配置日志:根据参数调整日志格式或级别。
  • 权限控制:通过参数指定角色(如 @require_role("admin"))。
  • 缓存策略:通过参数设置缓存超时时间(如 @cached(timeout=60))。
5. 完整示例import functoolsdef require_role(role): def decorator(func): @functools.wraps(func) def wrapper(user, *args, kwargs): if user.role != role: raise PermissionError(f"User must be {role}") return func(user, *args, kwargs) return wrapper return decoratorclass User: def __init__(self, name, role): self.name = name self.role = role@require_role("admin")def delete_database(user): return "Database deleted by " + user.nameadmin = User("Alice", "admin")guest = User("Bob", "guest")print(delete_database(admin)) # 正常执行print(delete_database(guest)) # 抛出 PermissionError总结

带参数的装饰器通过函数嵌套实现灵活配置,核心步骤为:

  1. 定义外层函数接收参数。
  2. 返回传统装饰器(接收 func)。
  3. 在包装函数中结合参数与原始函数逻辑。

这种模式在需要动态调整装饰器行为的场景中非常有用,同时保持了代码的简洁性和可复用性。