当前位置:首页 > Python > 正文

Python带参数装饰器详解 - 如何编写和使用带参数的Python装饰器

Python带参数装饰器详解

掌握Python高级编程技巧:编写和使用带参数的装饰器

什么是装饰器?

装饰器是Python中一种强大的功能,允许在不修改原始函数代码的情况下扩展其行为。它们本质上是接受函数作为参数并返回新函数的函数。

为什么需要带参数的装饰器?

标准装饰器适用于所有函数使用相同配置的情况。但当我们需要根据情况调整装饰器的行为时,就需要带参数的装饰器。例如:

  • 根据日志级别记录不同信息
  • 设置不同的超时时间
  • 根据环境切换缓存策略
  • 为不同用户设置访问权限级别

基础装饰器回顾

在深入带参数的装饰器之前,先回顾一下普通装饰器的结构:

def simple_decorator(func):
    def wrapper(*args, **kwargs):
        print("函数执行前操作")
        result = func(*args, **kwargs)
        print("函数执行后操作")
        return result
    return wrapper

@simple_decorator
def greet(name):
    print(f"Hello, {name}!")

# 调用
greet("Alice")

这个装饰器为函数添加了前后操作,但所有使用它的函数都会执行相同的操作。

带参数装饰器的结构

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

def decorator_with_args(arg1, arg2):
    # 外层处理装饰器参数
    def actual_decorator(func):
        # 中间层接受函数
        def wrapper(*args, **kwargs):
            # 内层包装函数
            # 在这里可以使用arg1, arg2
            result = func(*args, **kwargs)
            return result
        return wrapper
    return actual_decorator

结构解析:

  1. 外层函数:接受装饰器的参数,返回实际装饰器
  2. 中间层:接受被装饰的函数,返回包装函数
  3. 内层函数:执行装饰逻辑,调用原始函数

实用示例

示例1:重试装饰器

def retry(max_attempts=3, delay=1):
    def decorator(func):
        import time
        def wrapper(*args, **kwargs):
            attempts = 0
            while attempts < max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    print(f"尝试 {attempts}/{max_attempts} 失败,{delay}秒后重试...")
                    if attempts == max_attempts:
                        raise
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(max_attempts=5, delay=2)
def fetch_data(url):
    # 模拟可能失败的操作
    if "example" not in url:
        raise ValueError("无效URL")
    return "数据获取成功"

# 测试
print(fetch_data("https://test.com"))  # 会重试5次
print(fetch_data("https://example.com/data"))  # 成功

示例2:权限验证装饰器

def requires_role(role="user"):
    def decorator(func):
        def wrapper(user, *args, **kwargs):
            if user.get("role") != role:
                raise PermissionError(f"需要{role}权限,当前是{user.get('role')}权限")
            return func(user, *args, **kwargs)
        return wrapper
    return decorator

# 用户数据
admin_user = {"name": "Alice", "role": "admin"}
regular_user = {"name": "Bob", "role": "user"}

@requires_role("admin")
def delete_user(user):
    print(f"{user['name']} 删除了用户")

# 测试
delete_user(admin_user)  # 成功
try:
    delete_user(regular_user)  # 抛出异常
except PermissionError as e:
    print(f"错误: {e}")

高级应用

基于环境的装饰器

def environment_aware(prod_action, dev_action="bypass"):
    def decorator(func):
        import os
        def wrapper(*args, **kwargs):
            env = os.getenv("APP_ENV", "development")
            
            if env == "production":
                # 在生产环境执行特定操作
                print(f"生产环境操作: {prod_action}")
            else:
                # 在开发环境执行不同操作
                print(f"开发环境操作: {dev_action}")
                
            return func(*args, **kwargs)
        return wrapper
    return decorator

@environment_aware(prod_action="严格验证", dev_action="跳过验证")
def process_request(request):
    print("处理请求:", request)

# 设置环境变量
import os
os.environ["APP_ENV"] = "production"
process_request("重要请求")  # 生产环境模式

os.environ["APP_ENV"] = "development"
process_request("测试请求")  # 开发环境模式

注意事项与最佳实践

1. 保留函数元数据

使用@functools.wraps保留原始函数的元数据:

import functools

def debug_decorator(log_level="INFO"):
    def decorator(func):
        @functools.wraps(func)  # 保留元数据
        def wrapper(*args, **kwargs):
            print(f"[{log_level}] 调用 {func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

2. 装饰器堆叠顺序

当使用多个装饰器时,顺序很重要:

@decorator1
@decorator2
def my_function():
    pass

# 等同于
my_function = decorator1(decorator2(my_function))

3. 避免过度使用

装饰器虽强大,但过度使用会使代码难以理解和调试。仅在确实能简化代码或提高可维护性时使用。

掌握带参数装饰器,提升你的Python编程技能!

发表评论