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

在带参数的函数中使用Python装饰器 - 详细教程

Python带参函数中使用装饰器的高级指南

装饰器是Python中一种强大的功能,它允许开发者修改或增强函数的行为而不改变函数本身。本教程将重点介绍如何在带参数的函数中使用装饰器,包括装饰器的基础知识、带参数的装饰器实现以及在类方法中的应用。

一、装饰器基础回顾

在深入带参函数之前,我们先回顾装饰器的基本工作原理:

def simple_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

@simple_decorator
def greet():
    print("Hello, World!")

greet()

二、在带参函数中使用装饰器

当被装饰的函数带有参数时,我们需要在装饰器的内部函数中处理这些参数:

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")
        print(f"参数: {args}, {kwargs}")
        result = func(*args, **kwargs)
        print(f"返回值: {result}")
        return result
    return wrapper

@log_decorator
def add(a, b):
    return a + b

@log_decorator
def greet(name, message="你好"):
    return f"{message}, {name}!"

print(add(5, 3))
print(greet("张三", message="欢迎"))

关键点解析:

  • 使用*args**kwargs接受任意参数
  • 在内部函数中调用原始函数时传递所有参数
  • 返回原始函数的执行结果

三、带参数的装饰器

装饰器本身也可以接受参数,这需要三层嵌套函数:

def repeat(n_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(n_times=3)
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("李四")

工作流程:

  1. repeat(n_times) 返回装饰器函数
  2. 装饰器函数接受被装饰的函数作为参数
  3. 内部wrapper函数处理函数调用

四、类方法中的装饰器

在类中使用装饰器时,需要处理self参数:

def method_decorator(func):
    def wrapper(self, *args, **kwargs):
        print(f"调用方法: {self.__class__.__name__}.{func.__name__}")
        return func(self, *args, **kwargs)
    return wrapper

class Calculator:
    @method_decorator
    def add(self, a, b):
        return a + b
    
    @method_decorator
    def multiply(self, a, b):
        return a * b

calc = Calculator()
print(calc.add(10, 20))
print(calc.multiply(5, 6))

五、保留函数元信息

使用装饰器后,原始函数的元信息会被覆盖,使用functools.wraps解决:

from functools import wraps

def preserve_metadata(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """包装函数的文档字符串"""
        print(f"调用函数: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@preserve_metadata
def example(a: int, b: int) -> int:
    """两个数字相加"""
    return a + b

print(example.__name__)      # 输出 'example'
print(example.__doc__)       # 输出 '两个数字相加'
print(example.__annotations__)  # 输出 {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}

六、实际应用场景

1. 缓存函数结果

def cache(func):
    cached_results = {}
    @wraps(func)
    def wrapper(*args, **kwargs):
        key = (args, frozenset(kwargs.items()))
        if key not in cached_results:
            cached_results[key] = func(*args, **kwargs)
        return cached_results[key]
    return wrapper

@cache
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))

2. 权限验证

def requires_role(role):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            user = kwargs.get('user', None)
            if user and user.get('role') == role:
                return func(*args, **kwargs)
            else:
                raise PermissionError(f"需要 {role} 权限")
        return wrapper
    return decorator

@requires_role('admin')
def delete_user(user_id, user=None):
    print(f"删除用户 {user_id}")

admin_user = {'name': 'Admin', 'role': 'admin'}
regular_user = {'name': 'User', 'role': 'user'}

delete_user(123, user=admin_user)  # 成功
delete_user(456, user=regular_user)  # 抛出异常

总结

装饰器是Python中强大且灵活的工具,特别是在处理带参数的函数时。关键要点包括:

✓ 使用*args**kwargs处理任意参数
✓ 使用functools.wraps保留函数元数据
✓ 三层嵌套函数实现带参数的装饰器
✓ 在类方法中正确处理self参数

掌握这些技巧将大大提高代码的可重用性和可维护性。

发表评论