第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()

执行顺序

  1. 调用my_decorator("hello", "world"),返回decorator函数
  2. 调用decorator(my_function),返回wrapper函数
  3. wrapper赋值给my_function
  4. 调用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程序至关重要。

« 上一篇 装饰器基础 下一篇 » 类装饰器