检查函数是否被调用为装饰器

问题描述 投票:6回答:2

在下面的最小例子中,decorate被调用两次。首先使用@decorate,第二个使用正常函数调用decorate(bar)

def decorate(func):
    print(func.__name__)
    return func

@decorate
def bar():
    pass

decorate(bar)

如果通过使用decorate或作为普通函数调用调用调用,是否有可能看到@decorate内部?

python python-3.x decorator python-decorators
2个回答
9
投票

@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

2
投票

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")
© www.soinside.com 2019 - 2024. All rights reserved.