如何在 Python 中创建接受参数的装饰器

PythonBeginner
立即练习

简介

Python 装饰器是增强代码功能的强大工具。在本教程中,我们将深入探讨接受参数的装饰器概念,研究其语法、结构和实际用例。在本指南结束时,你将扎实理解如何在 Python 项目中创建和使用带参数的装饰器。

理解 Python 装饰器

Python 装饰器是一项强大的功能,它允许你在不改变函数源代码的情况下修改其行为。装饰器是一种用另一个函数包装一个函数的方式,为原始函数添加额外的功能。

装饰器使用 @ 符号定义,后面跟着装饰器函数名,放在函数定义之前。当一个函数被装饰时,装饰器函数会以原始函数作为参数被调用,然后它可以返回一个新的函数来替换原始函数。

下面是一个简单的装饰器示例,它记录传递给函数的参数:

def log_args(func):
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__},参数为 args={args},kwargs={kwargs}")
        return func(*args, **kwargs)
    return wrapper

@log_args
def add_numbers(a, b):
    return a + b

result = add_numbers(2, 3)
print(result)

输出:

调用 add_numbers,参数为 args=(2, 3),kwargs={}
5

在这个示例中,log_args 装饰器函数接受一个函数作为参数,并返回一个新的函数(wrapper),该函数在调用原始函数之前记录参数。

装饰器可用于实现各种功能,如缓存、日志记录、认证等等。它们是 Python 程序员工具包中的强大工具,理解它们的工作原理是成为一名熟练的 Python 开发者的重要组成部分。

带参数的装饰器:语法与结构

虽然基本的装饰器语法很简单,但 Python 也允许你创建接受参数的装饰器。当你想要自定义装饰器的行为或使其更灵活时,这会很有用。

带参数的装饰器的语法如下:

def decorator_with_args(arg1, arg2,...):
    def decorator(func):
        def wrapper(*args, **kwargs):
            ## 对参数进行一些操作
            return func(*args, **kwargs)
        return wrapper
    return decorator

@decorator_with_args(arg1, arg2,...)
def my_function(a, b):
    ## 函数代码
    pass

在这个示例中,decorator_with_args 函数接受一些参数,并返回一个可以应用于其他函数的装饰器函数。反过来,这个装饰器函数又返回一个新的函数(wrapper),可用于包装原始函数(my_function)。

下面是一个装饰器的示例,它允许你为函数设置缓存超时时间:

def cache_with_timeout(timeout):
    def decorator(func):
        cache = {}
        def wrapper(*args, **kwargs):
            key = (args, tuple(kwargs.items()))
            if key in cache and time.time() - cache[key][1] < timeout:
                return cache[key][0]
            else:
                result = func(*args, **kwargs)
                cache[key] = (result, time.time())
                return result
        return wrapper
    return decorator

@cache_with_timeout(60)
def expensive_function(a, b):
    ## 一些耗时的计算
    time.sleep(2)
    return a + b

print(expensive_function(2, 3))  ## 第一次调用需要2秒
print(expensive_function(2, 3))  ## 第二次调用立即返回缓存结果

在这个示例中,cache_with_timeout 装饰器接受一个 timeout 参数,该参数用于确定函数结果应缓存多长时间。wrapper 函数在调用原始函数之前检查缓存,并使用新结果和时间戳更新缓存。

带参数的装饰器提供了一种使装饰器更灵活、更可复用的方法,允许你根据传递给它们的参数来自定义其行为。

带参数装饰器的实际用例

带参数的装饰器可用于各种实际场景,为你的 Python 代码增添灵活性和可定制性。以下是一些示例:

缓存与记忆化

如前例所示,带参数的装饰器可用于实现缓存和记忆化。通过允许将缓存超时时间作为参数指定,你可以创建一个更灵活的缓存解决方案,根据应用程序的特定需求进行定制。

日志记录与调试

带参数的装饰器可用于为函数添加日志记录或调试功能。例如,你可以创建一个装饰器,使用指定的日志级别记录函数调用,或者一个向日志中添加计时信息的装饰器。

def log_function_call(log_level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            logger.log(log_level, f"调用 {func.__name__},参数为 args={args},kwargs={kwargs}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@log_function_call(logging.INFO)
def my_function(a, b):
    ## 函数代码
    pass

认证与授权

带参数的装饰器可用于对你的函数进行认证和授权检查。例如,你可以创建一个装饰器,要求特定的用户角色或权限级别才能访问函数。

def require_role(required_role):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if current_user.role!= required_role:
                raise PermissionError(f"用户必须具有 '{required_role}' 角色才能访问此函数。")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@require_role("admin")
def admin_only_function():
    ## 函数代码
    pass

可配置行为

带参数的装饰器可用于使你的函数更具可配置性,并适应不同的用例。例如,你可以创建一个装饰器,允许你指定函数的输入和输出格式,或者一个设置默认参数值的装饰器。

def with_input_output_formats(input_format, output_format):
    def decorator(func):
        def wrapper(*args, **kwargs):
            ## 将输入转换为预期格式
            args = [input_format(arg) for arg in args]
            kwargs = {k: input_format(v) for k, v in kwargs.items()}

            ## 调用原始函数
            result = func(*args, **kwargs)

            ## 将输出转换为预期格式
            return output_format(result)
        return wrapper
    return decorator

@with_input_output_formats(int, str)
def add_numbers(a, b):
    return a + b

print(add_numbers("2", "3"))  ## 输出: "5"

这些只是 Python 中带参数装饰器实际用例的几个示例。通过利用这个强大的功能,你可以创建更灵活、可复用和可维护的代码,以适应应用程序的特定需求。

总结

对于任何 Python 程序员来说,掌握带参数的装饰器都是一项很有价值的技能。在这个全面的教程中,我们涵盖了 Python 装饰器的基础知识、创建接受参数的装饰器的语法和结构,以及可以利用此功能的实际用例。通过理解和应用这些概念,你可以提高 Python 代码的灵活性、模块化和可维护性,最终提升你的整体编程技能。