python装饰器(Decorator)再谈
装饰器一直以来都是Python中很有用、很经典的一个feature。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):
(f"Executing function: {func.__name__}")
result = func(*args, **kwargs)
(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!