Python 复杂的装饰器

  • Post category:Python

Python的装饰器是一种非常实用的语言特性,可以在不改变原有函数代码的基础上,为其增加新的功能。在Python中,一个装饰器本质上是一个函数,它接收一个函数作为参数,返回一个新的函数。通过把原始函数作为参数传递到装饰器函数中以及定义新函数,可以在不修改原始函数的前提下为其添加新的功能。

装饰器的基本使用方式

下面是一个最简单的装饰器使用样例,假设有一个函数需要在调用前输出一条日志信息:

def log_before(func):
    def wrapper(*args, **kwargs):
        print("准备调用函数")
        return func(*args, **kwargs)

    return wrapper

@log_before
def my_func(x, y):
    print(x + y)

my_func(1, 2)

输出结果会先打印 “准备调用函数”,然后再执行my_func函数,输出3。这个样例中,我们定义了一个叫做log_before的装饰器函数,它可以接收任何函数作为参数,并在调用函数之前输出一定的信息。同时,我们还可以使用“@log_before”的语法糖,将my_func函数装饰上这个装饰器。

从这个样例中可以看到,装饰器的写法实际上就是定义一个函数,接收一个函数作为参数,并返回一个新的函数。在新的函数中,可以插入任何想要带有的代码,比如输出日志,统计函数执行时间等等。

带有参数的装饰器

除了基本的装饰器语法之外,Python还支持带有参数的装饰器。这种装饰器通常被称为“工厂函数”,因为它本身返回一个装饰器函数。

下面是一个示例,这个装饰器可以接收一个字符串参数,然后在调用被装饰的函数之前输出这个字符串:

def log_with_message(msg):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(msg)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@log_with_message("Before calling my_func")
def my_func(x, y):
    print(x + y)

my_func(1, 2)

此时运行这段代码,会先输出 “Before calling my_func”,然后执行my_func函数输出3。

从这个示例中可以看到,装饰器本身也可以接收参数,这让装饰器的功能更加多样化,能够根据不同的参数,为被修饰的函数添加不同的功能。

不带括号和参数的装饰器

如果装饰器函数没有参数,则在使用“@装饰器函数名”的时候,可以不加括号。下面是一个示例:

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

此时运行代码,会输出以下结果:

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

这个例子中,我们定义了一个装饰器my_decorator,它不接收参数。我们使用“@my_decorator”将say_hello函数装饰上它。当我们调用say_hello函数时,实际上执行的是my_decorator函数返回的wrapper函数,也就是在say_hello函数调用前打印一些信息,调用say_hello函数,然后再打印一些信息。

总结

Python的装饰器是一个非常重要的语言特性,能够实现函数的动态增强,使得我们可以更加简单地在不同的场景下使用同一个函数。在开发过程中,我们可以使用装饰器来增加函数的重试、缓存、出错重试、授权等能力,提高代码的灵活性和可维护性。同时,我们也可以使用闭包和装饰器来实现一些Python中原生不支持的特性,比如延迟绑定、懒计算等等。在掌握了装饰器的基本用法和相关概念之后,我们还可以根据具体的业务场景和需求,定义出各种不同形态、不同行为的装饰器函数。