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

Python带参数装饰器详解 - 深入理解与应用

Python带参数装饰器详解

掌握高阶Python技巧:创建和使用带参数装饰器

装饰器基础回顾

装饰器是Python中一种强大的语法特性,允许在不修改原函数代码的情况下增强其功能。带参数的装饰器则更进一步,提供了更大的灵活性。

装饰器的基本结构

标准装饰器是一个接受函数作为参数并返回新函数的可调用对象:

def decorator(func):
    def wrapper(*args, **kwargs):
        # 在调用函数前的操作
        result = func(*args, **kwargs)
        # 在调用函数后的操作
        return result
    return wrapper

@decorator
def my_function():
    pass

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

当我们希望装饰器本身可以接受参数来定制其行为时,就需要带参数的装饰器。例如:

  • 指定重试操作的次数
  • 设置缓存过期时间
  • 自定义日志格式
  • 控制权限级别

带参数装饰器的实现原理

带参数装饰器实际上是一个返回装饰器的函数:

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

@decorator_with_args('value1', 'value2')
def my_function():
    pass

这种三层嵌套结构是带参数装饰器的关键:

  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"Attempt {attempts} failed: {e}")
                    if attempts < max_attempts:
                        time.sleep(delay)
            raise RuntimeError(f"Function failed after {max_attempts} attempts")
        return wrapper
    return decorator

# 使用带参数的装饰器
@retry(max_attempts=5, delay=2)
def connect_to_database():
    # 模拟数据库连接
    import random
    if random.random() < 0.7:
        raise ConnectionError("Database connection failed")
    return "Connected successfully"

# 测试
print(connect_to_database())

示例2:带单位的计时装饰器

创建一个计时装饰器,可以指定时间单位:

import time

def timer(unit='seconds'):
    def decorator(func):
        def wrapper(*args, **kwargs):
            start = time.perf_counter()
            result = func(*args, **kwargs)
            end = time.perf_counter()
            duration = end - start
            
            # 根据单位转换时间
            if unit == 'milliseconds':
                duration *= 1000
                unit_str = 'ms'
            elif unit == 'microseconds':
                duration *= 1000000
                unit_str = 'μs'
            else:
                unit_str = 's'
            
            print(f"{func.__name__} executed in {duration:.4f} {unit_str}")
            return result
        return wrapper
    return decorator

# 使用带参数的装饰器
@timer(unit='milliseconds')
def calculate_sum(n):
    return sum(range(n))

# 测试
print(calculate_sum(1000000))

示例3:权限控制装饰器

创建一个权限控制装饰器,根据用户角色控制函数访问:

def requires_role(role):
    def decorator(func):
        def wrapper(user, *args, **kwargs):
            if user.get('role') != role:
                raise PermissionError(f"User does not have {role} 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, user_id):
    print(f"User {user['name']} deleted user {user_id}")
    return True

# 测试
try:
    # 管理员可以删除用户
    delete_user(admin_user, 1001)
    
    # 普通用户尝试删除会抛出异常
    delete_user(regular_user, 1002)
except PermissionError as e:
    print(f"Error: {e}")

带参数装饰器的应用场景

调试与日志

创建带日志级别参数的装饰器,控制不同情况下的日志输出详细程度。

性能监控

创建带阈值参数的装饰器,仅在执行时间超过指定阈值时发出警告。

API限流

创建带速率限制参数的装饰器,控制函数在指定时间内的最大调用次数。

缓存管理

创建带过期时间参数的装饰器,自动缓存函数结果并在指定时间后失效。

使用带参数装饰器的注意事项

  • 保留函数元数据:使用@functools.wraps保留原始函数的名称和文档字符串
  • 避免过度嵌套:三层嵌套结构可能影响可读性,考虑使用类实现装饰器
  • 参数顺序:装饰器参数必须位于函数参数之前
  • 调试复杂性:带参数装饰器增加了调试难度,确保添加清晰的日志
  • 性能影响:装饰器会增加函数调用开销,在性能敏感场景需谨慎使用

© 2023 Python高级编程技巧 | 带参数装饰器详解

发表评论