python装饰器(Decorator)再谈

python装饰器(Decorator)再谈

装饰器一直以来都是Python中很有用、很经典的一个feature。我以前的博文https://blog.csdn.net/cnds123/article/details/115588075,已经介绍过。python中装饰器比较强大,初学时不太好懂,现在换个路子再次介绍。

Python中,函数可以接受一个函数作为参数并返回一个新的函数作为结果

例子

def double(fn):
    def new_fn(*args, **kwargs):
        return 2 * fn(*args, **kwargs)
    return new_fn

def add(x, y):
    return x + y

# 使用 double 函数对 add 函数进行扩展
add_twice = double(add)

print(add_twice(1, 2)) # 输出:6 

在上面的代码中,我们定义了一个名为double的函数,它接收一个函数fn作为参数,并返回一个新函数new_fn。new_fn 函数会先调用原有的 fn 函数来获取结果,然后将该结果乘以 2,最后将乘以 2 的结果返回。

在主程序中,我们首先定义了一个名为add的函数,它接收两个数字作为参数,并返回它们的和。然后,我们使用 double 函数对 add 函数进行扩展,得到一个新的函数 add_twice。最后,我们调用 add_twice 函数来计算 1 + 2 的两倍,输出结果为 6。

为了简化语法,Python 引入了装饰器

上面这个例子改用装饰器,代码更简洁和易读:

def double(func):
    def wrapper(*args, **kwargs):
        result =2 * func(*args, **kwargs)        
        return result
    return wrapper
	
@double
def add(x, y):
    return x + y

print(add(1, 2)) # 输出:6

在上面的代码中,我们定义了一个名为double的装饰器函数,它接收一个函数func作为参数,并返回一个新函数wrapper。在wrapper函数中,我们首先打印出被装饰函数func的名称,然后调用func函数,并将其返回值保存到变量result中。最后,我们再次打印出函数名称以及其返回值,并将其返回给调用者。

通过在add函数的定义之前使用@double语法糖,我们实际上将add函数传递给了double函数,并获取了经过装饰之后的新函数。当我们调用被装饰的add函数时,实际上会调用wrapper函数。

为了简化语法,Python 引入了装饰器语法糖——@decorator_name,它可以让我们直接在函数定义时使用装饰器,并自动帮助我们生成新的函数,并将新函数绑定到原函数名上,例如:

@my_decorator # 直接在函数定义时使用装饰器语法糖
def my_func():
    # 原函数代码

这样,当我们在其他地方调用 my_func() 函数时,实际上调用的是经过装饰器包装过的新函数,而不是原函数本身,而且我们无需显式调用装饰器函数来生成新的函数。这种方式不仅简化了代码,而且也让代码更易于理解和维护。

Python中的装饰器是一种高级的语法技巧,用于修改或扩展函数的功能。装饰器可以在不修改原始函数代码的情况下,通过添加额外的功能来包装函数。

具体而言,装饰器是一个函数,它接受一个函数作为参数,并返回一个新的函数作为结果。新函数通常会在调用原始函数之前或之后执行一些额外的操作,例如记录日志、验证参数、缓存结果等。在 Python 中,装饰器允许我们在不修改原函数代码的情况下,动态地向一个函数添加额外的功能。Python 的装饰器语法形式如下:
@decorator
def function():
    …

其中:

decorator 是一个装饰器函数的名称。

function 是待装饰的函数名称。

这种语法称为装饰器的语法糖,它等价于以下形式:
def function():
    …

function = decorator(function)
 

可以看到,使用装饰器语法后,实际上是将装饰器函数直接应用到待装饰的函数上,从而对其进行了修改或扩展。

注意,装饰器不仅可以装饰普通函数,还可以装饰类方法、静态方法甚至类本身。对于不同类型的函数,装饰器在使用上的区别较小,只需要保证装饰器的适用性和功能实现即可。

【语法糖(Syntactic Sugar)是指一种编程语言中的特殊语法,它可以让程序员用更加简洁、易读的方式来表达某些常见的编程模式或操作。尽管这些语法糖并不影响编程语言本身的功能和能力,但却可以显著地提高代码的可读性、可维护性和开发效率。】

简单示例如下:

# 定义装饰函数
def decorator(func):
    def wrapper(*args, **kwargs):
        # 在调用原始函数之前可以执行一些操作
        print("Before calling the function")
        # 调用原始函数
        result = func(*args, **kwargs)
        # 在调用原始函数之后可以执行一些操作
        print("After calling the function")
        # 返回函数结果
        return result
    # 返回新的函数作为装饰器的结果
    return wrapper

# 定义被装饰的函数
@decorator
def my_function():
    print("The my_function")
    print("Hello world")

# 调用被装饰的函数
my_function()

以上是一个简单的装饰器示例,decorator 是一个装饰器函数,wrapper 是装饰器返回的新函数。通过 @decorator 的语法糖,我们将装饰器应用到了 my_function 上。运行时,调用 my_function() 实际上会执行 decorator 函数内部的 wrapper 函数,并在调用前后输出相应的信息:

Before calling the function
The my_function
Hello world
After calling the function

 下面给出有点使用价值的例子:

import logging #导入Python标准库中的logging模块

# 配置日志输出到文件
logging.basicConfig(filename='app.log', level=logging.INFO)

def log_function_execution(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Executing function: {func.__name__}")
        result = func(*args, **kwargs)
        logging.info(f"Function execution completed.")
        return result
    return wrapper

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

# 使用装饰器后调用函数
result = add_numbers(2, 3)
print(result) #5

上述代码中,我们定义了一个名为 log_function_execution 的装饰器函数,它接受一个函数作为参数,并返回一个新的包装函数 wrapper。在 wrapper 函数内部,我们首先输出了即将执行的函数名,并调用原始函数 func,并且记录函数执行完成后的信息。然后,通过使用 @log_function_execution 将装饰器应用到 add_numbers 函数上。这样,在调用 add_numbers 函数时,实际上会执行经过装饰器包装后的 wrapper 函数,从而实现了在函数执行前后进行日志记录的功能。

这样,在执行 add_numbers(2, 3) 时,会先打印出执行函数的信息,然后计算结果并返回。同时,也会输出函数执行完成的日志信息。日志信息会被写入 'app.log' 文件中,你可以打开该文件来查看日志内容。

如果有多个装饰器同时装饰一个函数,那么最内层的装饰器会最先执行,最外层的装饰器会最后执行。例如:

def decorator1(func):
    print("decorator1 executed")
    def wrapper(*args, **kwargs):
        print("decorator1 before function execution")
        result = func(*args, **kwargs)
        print("decorator1 after function execution")
        return result
    return wrapper

def decorator2(func):
    print("decorator2 executed")
    def wrapper(*args, **kwargs):
        print("decorator2 before function execution")
        result = func(*args, **kwargs)
        print("decorator2 after function execution")
        return result
    return wrapper

@decorator1
@decorator2
def my_function():
    print("my_function executed")

my_function()

输出结果为:

decorator2 executed
decorator1 executed
decorator1 before function execution
decorator2 before function execution
my_function executed
decorator2 after function execution
decorator1 after function execution

可以发现装饰器 decorator2 是最内层的装饰器,因此它首先执行,然后返回包装函数 wrapper2。接着,装饰器 decorator1 在 decorator2 的基础上继续进行装饰,返回的包装函数 wrapper1。最终,函数 my_function 被包装在 wrapper1 中,并按照装饰器的顺序逐层执行。

小结一下
装饰器本质上是一个函数,接受一个函数作为参数,并返回一个新的函数作为结果。通过使用装饰器,可以在在不修改原始函数的情况下,可以添加一些额外的功能、逻辑或约束条件。
不使用装饰器时,我们可以显式地调用一个装饰器函数来生成新的函数(即包装原函数的新函数),然后再将这个新函数绑定到原函数名上,从而完成函数修饰的过程,例如:
def my_decorator(func):
    def wrapper(*args, **kwargs):
        # 添加额外的功能
        return func(*args, **kwargs)
        # 添加额外的功能
    return wrapper

def my_func():
    # 原函数代码
my_func = my_decorator(my_func) # 显式调用装饰器函数生成新函数并重新绑定到原函数名上

my_func([参数列表]) #调用

用装饰器时,可写为:
def my_decorator(func):
    def wrapper(*args, **kwargs):
        # 添加额外的功能
        return func(*args, **kwargs)
        # 添加额外的功能
    return wrapper

@my_decorator
def my_func():
# 原函数代码

my_func([参数列表]) #调用

使用@decorator_name语法糖来应用装饰器。这样可以使代码更加简洁易懂,易于维护。

OK!

猜你喜欢

转载自blog.csdn.net/cnds123/article/details/131234738