Быстрый в изучении - мощный в программировании
>> Telegram ЧАТ для Python Программистов

Свободное общение и помощь советом и решением проблем с кодом! Заходите в наш TELEGRAM ЧАТ!

>> Python Форум Помощи!

Мы создали форум где отвечаем на все вопросы связанные с языком программирования Python. Ждем вас там!

>> Python Канал в Telegram

Обучающие статьи, видео и новости из мира Python. Подпишитесь на наш TELEGRAM КАНАЛ!

Вложение декораторов в Python

Вложение декораторов в Python

Иногда одного декоратора бывает недостаточно. Для поддержки многоступенчатых расширений синтаксис декораторов позволяет добавлять несколько уровней обертывающей логики к декорируемой функции или методу. При использовании такой возможности каждый декоратор должен указываться в отдельной строке. Синтаксическая конструкция следующего вида:

@AAA
@BBB
@CCC
def function(...):
    ...

равноценна следующей:

def f(...):
    ...
f = AAA(BBB(CCC(f)))

Здесь оригинальная функция передается трем различным декораторам, а получившийся в результате вызываемый объект присваивается оригинальному имени. Каждый декоратор обрабатывает результат, возвращаемый предыдущим декоратором, который может быть оригинальной функцией или объектом-оберткой. Если все декораторы возвращают обертки, то при вызове функции по оригинальному имени будет выполнена логика всех трех обертывающих объектов, расширяя возможности функции тремя различными способами.

Последний декоратор в списке будет задействован первым и окажется самым глубоко вложенным.

Так же как и в случае декорирования функций, применение нескольких декораторов классов приведет к вызову нескольких вложенных функций и, возможно, к созданию нескольких уровней обертывающей логики вокруг операции создания экземпляров. Например, следующий фрагмент:

@firsdecorator
@seconddecorator
class CDC:
    ...
 
MyInstance = CDC()

эквивалентен следующему:

class CDC:
    ...
C = firstdecorator(seconddecorator(CDC))
 
XX = C()

Как и прежде, каждый декоратор может возвращать оригинальный класс или объект обертку. Если оба декоратора в примере возвращают обертки, то позднее, когда будет предпринята попытка создать экземпляр оригинального класса CDC, вызов будет направлен обертывающим объектам, созданным обоими декораторами, firstdecorator и seconddecorator, которые могут преследовать совершенно разные цели.

Например, следующие декораторы просто возвращают декорируемую функцию, не выполняя никаких дополнительных действий:

def fn1(arg): return arg
def fn2(arg): return arg
def fn3(arg): return arg
 
@fn1
@fn2
@fn3
def myfunc():    # myfunc = fn1(fn2(fn3(myfunc)))
    print('Hello')
 
myfunc()     # Выдает "Hello"

При применении аналогичных ничего не делающих декораторов к классам результат будет похожим.

Однако когда декораторы добавляют обертывающие объекты функций, они могут расширять возможности оригинальных функций - в следующем примере происходит объединение результатов в ходе выполнения уровней обертывающей логики декораторов от внутренней к внешней:

def fn1(arg): return lambda: 'XX' + arg()
def fn2(arg): return lambda: 'YY' + arg()
def fn3(arg): return lambda: 'ZZ' + arg()
 
@fn1
@fn2
@fn3
def myfunc():    # myfunc = fn1(fn2(fn3(myfunc)))
    return 'Python'
 
print(myfunc())    # Выведет "XXYYZZPython"

Для реализации обертывающей логики мы использовали здесь lambda-функции (каждая из них сохраняет обертываемую функцию в области видимости объемлющей функции). На практике обертки могут быть реализованы в виде функций, вызываемых классов и других объектов. В случае удачной конструкции декораторов возможность их вложения друг в друга дает нам возможность составлять из них самые разнообразные комбинации.

Оставьте комментарий!

Используйте нормальные имена.

Имя и сайт используются только при регистрации

Если вы уже зарегистрированы как комментатор или хотите зарегистрироваться, укажите пароль и свой действующий email. При регистрации на указанный адрес придет письмо с кодом активации и ссылкой на ваш персональный аккаунт, где вы сможете изменить свои данные, включая адрес сайта, ник, описание, контакты и т.д., а также подписку на новые комментарии.

(обязательно)