Синтаксис
@some_decorator
def somefunc():
...
эквивалентно:
somefunc = some_decorator(somefunc)
Это возможно в Python, потому что функции первоклассные объекты . Их можно передавать в качестве аргументов и возвращать из других функций - так называемые функции высшего порядка .
В вашем примере последний вызов multiply()
на самом деле запускает innerExector()
, функцию, созданную в декорированном определении multiply()
. Чтобы понять, что здесь происходит, присмотритесь к определению executor
:
def executor(func):
def innerExecutor():
#print("inside executor")
print("------------------------")
func()
print("------------------------")
return innerExecutor
При коде:
@executor
def multiply():
...
выполняется, функция executor
вызывается с функцией multiply
в качестве аргумента. executor
определяет функцию innerExecutor()
и возвращает ее , но не выполняет ее . Этот экземпляр innerExecutor()
вызывается вместо всех будущих ссылок на multiply()
.
Но подождите - обычно мы не хотим полностью заменять декорируемую функцию. innerExecutor()
необходимо вызвать декорированную функцию в какой-то момент. Напомним, что декорированная функция определения:
@executor
def multiply():
...
эквивалентно:
def multiply():
...
multiply = executor(multiply)
Передача функции, которая будет оформлена как аргумент декоратору, позволяет определению innerExecutor()
вызывать ее. Но подождите - innerExecutor()
не вызывается намного позже, после того, как executor()
вернулся и его аргумент func
вышел из области видимости. Так почему же это работает?
Благодаря еще одной функции Python, называемой замыканием . Это означает, что внутренняя функция может ссылаться на локальные переменные, определенные во вложенной области действия, включая аргументы, передаваемые во внешнюю функцию executor
- даже после выхода из охватывающей области действия . Волшебная особенность замыканий заключается в том, что интерпретатор обнаруживает зависимость определения внутренней функции от локальной переменной внешней функции и сохраняет ее доступной даже после возврата executor()
. Эта ссылка содержит более подробную информацию о замыканиях.
Наконец, вызов multiply()
в последней строке фактически вызывает экземпляр innerExecutor()
, который был создан при выполнении декорированного определения, что, в свою очередь, вызывает исходную неокрашенную версию multiply()
.
Каждый раз, когда декоратор используется для декорирования определения функции, он создает новый экземпляр внутренней функции, который заменяет недекорированную функцию. Таким образом, если вы украсите 5 разных функций, будет создано 5 разных экземпляров innerExecutor()
. Позже, когда вызывается любая из этих декорированных функций, она вместо этого вызывает вместо этого соответствующий экземпляр innerExecutor()
, который, в свою очередь, вызывает соответствующую недекорированную функцию.