В Python можно применить несколько декораторов к функции, укладывая их друг на друга. При использовании этой возможности каждый декоратор должен указываться в отдельной строке:
@decorator1 @decorator2 @decorator3 def func(): ...
Несколько декораторов, следующих подряд, не декорируют друг друга. Они изменяют функцию и ее измененный вариант в качестве аргумента передают друг другу в порядке следования.
Здесь декорируемая функция func()
передается трем различным декораторам decorator1
, decorator2
и decorator3
. Каждый декоратор обрабатывает результат, возвращаемый предыдущим декоратором. Последний декоратор в списке, в данном случае decorator3
, будет задействован первым.
Для более детального понимания, что происходит в примере выше, запишем его следующим образом:
func = decorator1(decorator2(decorator3(func)))
Другими словами, декораторы начинают обрабатывать функцию, к которой они применяются, изнутри. Пример со "stackoverflow":
def bold(func): def wrapper(): val = func() # Вставка HTML кода до и после 'val' return '<b>' + val + '</b>' return wrapper def italic(func): def wrapper(): val = func() # Вставка HTML кода до и после 'val' return '<i>' + val + '</i>' return wrapper @bold @italic def say(): return "hello" print(say()) # <b><i>hello</i></b>
Еще пример. Возьмем декораторы repeater()
и delayed()
из материала "Декораторы с аргументами" и применим их к функции, которая имитирует проверку сайта на доступность:
@repeater(repeat=3) @delayed(delay=30) def monitor(uri): print(f'Проверка {uri} на доступность') monitor('https://docs-python.ru/') # 1: Спим 30 сек. # Проверка https://docs-python.ru/ на доступность # 2: Спим 30 сек. # Проверка https://docs-python.ru/ на доступность # 3: Спим 30 сек. # Проверка https://docs-python.ru/ на доступность
Обратите внимание в какой последовательности выполняются декораторы. Сначала выполняется декоратор @delayed
- происходит задержка выполнения на 30 секунд, а затем выполняется декоратор @repeater
, который будет повторять предыдущую операцию 3 раза.
Попробуем изменить очередность декораторов в коде и логика выполнения поменяется:
@delayed(delay=30) @repeater(repeat=3) def monitor(uri): print(f'Проверка {uri} на доступность') monitor('https://docs-python.ru/') # Спим 30 сек. # 1: Проверка https://docs-python.ru/ на доступность # 2: Проверка https://docs-python.ru/ на доступность # 3: Проверка https://docs-python.ru/ на доступность
В случае удачной реализации декораторов и преимущество их вложения друг в друга, дает возможность составлять из них самые разнообразные комбинации.