如果我曾经在我的函数中使用了装饰器,我如何单独运行这个函数,而不嵌入装饰器功能中?
例如,我有一个函数
printArg
,它会打印一个参数。为了某些可用性,我需要将其与datetime.now()
“混合”。为此我写了一个装饰器timeCalled
。现在每次我调用 printArg
也会调用一个装饰器。
有没有办法单独调用
printArg
,这样我就不会重复自己并编写另一个没有装饰器功能的“printArg
”(datetime.now()
)?
from datetime import datetime
def timeCalled(func):
def wrapper(*args, **kwargs):
print(f'{datetime.now()}: Function called {func.__name__}')
result = func(*args, **kwargs)
return result
return wrapper
@timeCalled
def printArg(arg):
print(f'Your arg is {arg}')
printArg('Mama')
没有不依赖于实现细节的通用方法。在这个特殊情况中,由于
func
引用了wrapper
中的原始函数并且位于闭包中,因此您可以使用:
printArg.__closure__[0].cell_contents
检索它。
更一般地,在创建包装器时使用
functools.wraps
是一个很好的做法,这使得包装器函数“看起来像”原始函数。而且,它将原始函数添加到 __wrapped__
属性中,因此:
from datetime import datetime
from functools import wraps
def timeCalled(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f'{datetime.now()}: Function called {func.__name__}')
result = func(*args, **kwargs)
return result
return wrapper
@timeCalled
def printArg(name):
print(f'Your arg is {name=}.')
在这种情况下,您可以使用:
printArg.__wrapped__
装饰器只是被装饰函数的包装函数,其语法:
@timeCalled
def printArg(arg):
pass
相当于:
def printArg(arg):
pass
printArg = timeCalled(printArg)
因此,为了避免两次定义相同的函数,而只装饰一个函数,您可以定义该函数一次,然后使用传递给它的函数调用装饰器,并将返回的函数对象分配给不同的名称:
def printArg(arg):
print(f'Your arg is {name}')
timed_printArg = timeCalled(printArg)
这样就可以调用
timed_printArg('Mama')
来定时调用printArg('Mama')
,直接调用printArg('Mama')
来完成,不需要装饰器的参与。
给你的装饰函数起一个不同的名字。为此,不要使用
@
符号,而是使用:
decorated_printArg = timeCalled(printArg)