Понимаем декораторы в Python'e, шаг за шагом. Шаг 2

Понимаем декораторы в Python'e, шаг за шагом. Шаг 2

Итак, в первой части данной статьи мы совершили базовое знакомство с декораторами, принципами их работы и даже написали свой вручную. Однако, все декораторы, которые мы до этого рассматривали не имели одного очень важного функционала — передачи аргументов декорируемой функции. Что ж, исправим это недоразумение!

Передача («проброс») аргументов в декорируемую функцию

Никакой чёрной магии, всё, что нам необходимо — собственно, передать аргументы дальше!

* — Прим. переводчика: Питер Венкман — имя одного из Охотников за приведениями, главного героя одноименного культового фильма.

Декорирование методов

Один из важных фактов, которые следует понимать, заключается в том, что функции и методы в Python'e — это практически одно и то же, за исключением того, что методы всегда ожидают первым параметром ссылку на сам объект (self). Это значит, что мы можем создавать декораторы для методов так же, как и для функций, просто не забывая про self.

Конечно, если мы создаём максимально общий декоратор и хотим, чтобы его можно было применить к любой функции или методу, то стоит воспользоваться тем, что *args распаковывает список args, а **kwargs распаковывает словарь kwargs:

Вызов декоратора с различными аргументами

Отлично, с этим разобрались. Что вы теперь скажете о том, чтобы попробовать вызывать декораторы с различными аргументами?

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

Как мы видим, это два аналогичных действия. Когда мы пишем — мы просто говорим интерпретатору «вызвать функцию, под названием my_decorator». Это важный момент, потому что данное название может как привести нас напрямую к декоратору… так и нет! Давайте сделаем нечто страшное!:)

Длинно? Длинно. Перепишем данный код без использования промежуточных переменных:

А теперь ещё раз, ещё короче:

Вы заметили, что мы вызвали функцию, после знака "@"?:)

Вернёмся, наконец, к аргументам декораторов, ведь если мы используем функцию, чтобы создавать декораторы «на лету», мы можем передавать ей любые аргументы, верно?

* — Прим. переводчика: в данном примере автор упоминает имена главных героев популярного сериала «Теория Большого взрыва». Вот он, искомый декоратор, которому можно передавать произвольные аргументы. Безусловно, аргументами могут быть любые переменные:

Таким образом, мы можем передавать декоратору любые аргументы, как обычной функции. Мы можем использовать и распаковку через *args и **kwargs в случае необходимости. Но необходимо всегда держать в голове, что декоратор вызывается ровно один раз. Ровно в момент, когда Python импортирует Ваш скрипт. После этого мы уже не можем никак изменить аргументы, с которыми. Когда мы пишем "import x" все функции из x декорируются сразу же, и мы уже не сможем ничего изменить.

Немного практики: напишем декоратор декорирующий декоратор

Если вы дочитали до этого момента и ещё в строю — вот вам бонус от меня. Это небольшая хитрость позволит вам превратить любой обычный декоратор в декоратор, принимающий аргументы. Изначально, чтобы получить декоратор, принимающий аргументы, мы создали его с помощью другой функции. Мы обернули наш декоратор. Есть ли у нас что-нибудь, чем можно обернуть функцию? Точно, декораторы!

Давайте же немного развлечёмся и напишем декоратор для декораторов:

Это может быть использовано так:

Думаю, я знаю, что Вы сейчас чувствуете. Последний раз Вы испытывали это ощущение, слушая, как вам говорят: «Чтобы понять рекурсию необходимо для начала понять рекурсию». Но ведь теперь Вы рады, что разобрались с этим?;)

Рекомендации для работы с декораторами
  • Декораторы были введены в Python 2.4, так что узнавайте, на чём будет выполняться Ваш код.
  • Декораторы несколько замедляют вызов функции, не забывайте об этом.
  • Вы не можете «раздекорировать» функцию. Безусловно, существуют трюки, позволяющие создать декоратор, который можно отсоединить от функции, но это плохая практика. Правильней будет запомнить, что если функция декорирована — это не отменить.
  • Декораторы оборачивают функции, что может затруднить отладку.
Как можно использовать декораторы?

И в заключение, я бы хотел ответить на вопрос, который я часто слышу: зачем же нужны декораторы? Как их можно использовать? Декораторы могут быть использованы для расширения возможностей функций из сторонних библиотек (код которых мы не можем изменять), или для упрощения отладки (мы не хотим изменять код, который ещё не устоялся). Так же полезно использовать декораторы для расширения различных функций одним и тем же кодом, без повторного его переписывания каждый раз, например:

Таким образом, декораторы можно применить к любой функции, расширив её функционал и не переписывая ни строчки кода!

В Python включены такие декораторы как property, staticmethod и т.д. В Django декораторы используются для управления кешированием, контроля за правами доступа и определения обработчиков адресов. В Twisted — для создания поддельных асинхронных inline-вызовов. Декораторы открывают широчайший простор для экспериментов! И надеюсь, что данная статья поможет Вам в его освоении! Спасибо за внимание!

📎📎📎📎📎📎📎📎📎📎