Проверьте, была ли функция вызвана как декоратор - PullRequest
0 голосов
/ 05 сентября 2018

В следующем минимальном примере decorate вызывается два раза. Сначала с помощью @decorate, затем с помощью обычного вызова функции decorate(bar).

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

@decorate
def bar():
    pass

decorate(bar)

Можно ли увидеть внутри decorate, был ли вызов вызван с помощью @decorate или как обычный вызов функции?

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Ответ Оливье выкинул мысли из головы. Однако, поскольку 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")
0 голосов
/ 05 сентября 2018

Синтаксис @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 указывает, сколько строк кода вокруг текущей строки должно быть возвращено. В некоторых конкретных случаях, например, при украшении подкласса, текущая строка была в объявлении класса вместо декоратора. Точная причина такого поведения была исследована здесь.

Пример

@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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...