python中的装饰器decorator
装饰器:在代码运行期间动态增加功能的方式,decorator帮助跟踪和记录函数的执行,特别是在调试时。可以动态地修改函数行为,例如,可以实现缓存、授权、性能分析等功能,将代码逻辑分离出来,易于维护和扩展。
例1:
def log(func): #接收func函数为参数
def wrapper(*args,**kw):
print('call %s():'%func.__name__)
return func(*args,**kw) #原始封装的函数func
return wrapper
@log
def now():
print('2023-3-28')
m = now()
例2:
#decorator本身传入参数,需要写一个返回decorator 的高阶函数
def log(text):
def decorator(func):
def wrapper(*args,**kw):
print('%s %s():'%(text,func.__name__))
return func(*args,**kw)
return wrapper
return decorator
@log('execute')
def now():
print('2023-3-28')
nm = now()
print(now.__name__)
这段代码实现了一个装饰器(decorator)函数,该装饰器函数用于在执行被装饰函数时打印一些信息。具体来说,代码定义了一个名为log的函数,它接受一个字符串参数text。log函数返回一个内部函数decorator,该函数也是一个装饰器。
decorator函数接受一个函数作为参数,返回一个内部函数wrapper。wrapper函数接受任意数量的位置参数和关键字参数,并打印一条带有text和被装饰函数名称的信息。
然后,wrapper函数调用原始的被装饰函数,并返回其结果。在代码的末尾,我们使用装饰器@log(‘execute’)来装饰函数now。 这意味着当我们调用now()函数时,实际上会调用wrapper函数,它会打印一条日志信息,然后调用now()函数并返回结果。
最后,我们将nm变量设置为now()函数的结果,并打印nm变量的值。
例3:运用wraps函数
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args,**kw):
print('call %s():'%func.__name__)
return func(*args,**kw)
return wrapper
具体来说,functools.wraps函数是一个装饰器函数,它接受一个函数对象(func函数)作为参数,并返回一个新的函数对象, 该函数对象将被包装函数的元数据更新为与原始函数相同。
在这个例子中,我们使用functools.wraps来更新内部函数wrapper的元数据,将其设置为被装饰函数func的元数据。
为什么要更新wrapper元数据?
更新wrapper函数的元数据是为了**保留被装饰函数(func函数)**的信息,这包括函数名称、文档字符串、函数参数和注释等。这些元数据对于调试、测试和文档化非常重要,因为它们可以帮助我们理解和使用代码。
当我们使用装饰器来装饰函数时,装饰器函数(log函数:将函数func装饰成一个新函数wrapper,并在调用wrapper时,添加了额外的行为)实际上会替换原始函数,从而使得原始函数(func函数)的元数据无法被访问。为了解决这个问题,Python提供了functools.wraps函数,它可以将装饰器函数的元数据更新为原始函数的元数据。
如果没有更新wrapper函数的元数据,那么在调用help()函数或者使用其他工具来检查被装饰函数时,将会显示wrapper函数的元数据,而不是被装饰函数(func函数)的元数据。这可能会导致混淆和错误,因为我们需要的是被装饰函数的信息,而不是wrapper函数的信息。
因此,为了保留被装饰函数(func函数)的元数据,并使其可以正常工作,我们通常会使用functools.wraps函数来更新wrapper函数的元数据。这样,我们就可以在调用help()函数或使用其他工具时访问被装饰函数的元数据,而不会被wrapper函数的元数据所混淆。
例4:带参数的decorator
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args,**kw):
print('%s %s():'%(text,func.__name__))
return func(*args,**kw)
return wrapper
return decorator
@log('execute')
def mode():
print('2023-3-28')
s = mode()