在下面的最小例子中,decorate
被调用两次。首先使用@decorate
,第二个使用正常函数调用decorate(bar)
。
def decorate(func):
print(func.__name__)
return func
@decorate
def bar():
pass
decorate(bar)
如果通过使用decorate
或作为普通函数调用调用调用,是否有可能看到@decorate
内部?
@decorator
语法只是语法糖,因此两个示例都具有相同的行为。这也意味着你们之间所做的任何区别都可能没有你想象的那么有意义。
虽然,你可以使用inspect
来阅读你的脚本,看看如何在上面的框架中调用装饰器。
import inspect
def decorate(func):
# See explanation below
lines = inspect.stack(context=2)[1].code_context
decorated = any(line.startswith('@') for line in lines)
print(func.__name__, 'was decorated with "@decorate":', decorated)
return func
请注意,我们必须将context=2
指定为inspect.stack
函数。 context
参数指示必须返回当前行周围的代码行数。在某些特定情况下,例如在装饰子类时,当前行是在类声明而不是装饰器上。 The exact reason for this behaviour has been explored here.
@decorate
def bar():
pass
def foo():
pass
foo = decorate(foo)
@decorate
class MyDict(dict):
pass
bar was decorated with "@decorate": True
foo was decorated with "@decorate": False
MyDict was decorated with "@decorate": True
还有一些我们难以克服的极端情况,例如装饰器和类声明之间的换行符。
# This will fail
@decorate
class MyDict(dict):
pass
Olivier的回答让我的想法完全消失了。然而,由于inspect.stack()
是一个特别昂贵的电话,我会考虑选择使用以下内容:
frame = inspect.getframeinfo(inspect.currentframe().f_back, context=1)
if frame.code_context[0][0].startswith('@'):
print('Used as @decorate: True')
else:
print("Used as @decorate: False")