def mult(f):
return lambda x: func(x * x)
def add(func):
return lambda x: func(x + 4)
@mult
@add
def f1(x):
return x
@add
@mult
def f2(x):
return x
>>> f1(2)
8
>>> f2(2)
36
Из примера очевидно, что декораторы исполняются в порядке следования. В официальной документации об этом сказано что:
@f1(arg)
@f2
def func(): pass
эквивалентно
def func(): pass
func = f1(arg)(f2(func))
Гугл привел к потрясающе подробному и исчерпывающему раскрытию темы декораторов на stackoverflow. К тому же вопросу дан и более краткий ответ, который привожу здесь:
def makebold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
def makeitalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makebold
@makeitalic
def hello():
return "hello world"
print hello() ## returns "<b><i>hello world</i></b>"
Меня этот пример сбил с толку, т.к., если спроецировать на пример с числами, то можно сделать вывод, как будто при вызове f2(2)
должны выполняться действия ((2 * 2) + 4)
, что в результате дало бы 8, если выполнять в арифметическом порядке вложенности. Но интерпретатор выдает нам 36. Стало любопытно, как выглядит байт-код этой функции
>>> dis.dis(f2)
6 0 LOAD_DEREF 0 (func)
3 LOAD_FAST 0 (x)
6 LOAD_CONST 1 (4)
9 BINARY_ADD
10 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
13 RETURN_VALUE
Здесь видно, что сначала к аргументу прибавляется 4, а затем – с измененным аргументом – вызывается функция (которая декорируется еще раз). Следовательно, в примере с html-тегами сначала рисуется внешний тег <b>
, а затем внутренний <i>
. В общем суть моего непонимания была в следующем – несколько декораторов, следующих подряд, не декорируют друг друга. Они изменяют функцию, и передают ее в качестве аргумента друг другу в порядке следования.
Вот еще пара декораторов без всякой вложенности для большей наглядности:
def first(func):
def wrapper():
print("First")
func()
return wrapper
def second(func):
def wrapper():
print('Second')
func()
return wrapper
@first
@second
def myfunc():
print("Original function")
И вызовем функцию в консоли Python:
>>> myfunc()
First
Second
Original function
Мне стало понятней, а вам?