Что такое декоратор?
Декораторы - важная часть Python. Если коротко: они являются функциями, которые изменяют работу других функций. Они помогают делать код короче и более "питонистичным". Большинство новичков не знает, где их использовать, так что я расскажу о нескольких случаях, когда декораторы помогут написать лаконичный код.
Для начала рассмотрим, как написать свой собственный декоратор.
Это будет самым сложным моментом в теме, поэтому мы будем продвигаться шаг за шагом, так что вы сможете все полностью понять.
Все в Python является объектом
Для начала краткая ретроспектива функций в Python:
Определение функций внутри функций
Итак, это были основы работы с функциями. Теперь продвинемся на шаг дальше. В Python разрешено объявлять функции внутри других функций:
Теперь мы знаем, что возможно определять функции внутри других функций. Другими словами: мы можем создавать вложенные функции. Теперь вам нужно познакомиться с еще одной возможностью функций: возвращать другие функции.
Возвращение функции из функции
Нам не обязательно исполнять функцию, определенную внутри другой функции сразу, мы можем вернуть её в качестве возвращаемого значения:
Давайте еще раз пробежимся по коду. Через условный оператор мы возвращаем из функции объекты greet
и welcome
, а не greet()
и welcome()
. Почему? Потому что скобки означают вызов функции, без них мы просто передаем сам объект функции. Достаточно ясно? Давайте я чуть подробнее остановлюсь на этом. Когда мы пишем a = hi()
, функция hi()
исполняется и (поскольку имя по умолчанию yasoob) возвращается функция greet
. Если мы изменим код на a = hi(name="ali")
, то будет возвращена функция welcome
. Мы также можем набрать hi()()
, что вернет Вы внутри функции greet()
.
Передаем функцию в качестве аргумента другой функции
Теперь у нас есть все необходимые знания для изучения работы декораторов. Декораторы позволяют нам исполнять определенный код до и после исполнения конкретной функции.
Пишем наш первый декоратор
В прошлом примере мы по сути уже написали декоратор! Давайте изменим его и сделаем немного более полезным:
Все ясно? Мы просто использовали принципы, с которыми познакомились выше. Это то, чем и занимаются декораторы в Python! Они "обертывают" функцию и модифицируют её поведение определенным образом. Сейчас вы можете спросить, почему мы не используем в коде символ @. Это просто более короткий способ декорировать функции. Вот как мы можем модифицировать пример выше с использованием @:
Надеюсь, теперь у вас есть базовое представление о логике работы декораторов в Python. Однако, у нашего кода есть одна проблема. Если мы исполним:
Мы этого не ожидали! Имя функции должно быть a_function_requiring_decoration
. В реальности наша функция была заменена на wrapTheFunction. Она перезаписала имя и строку документации оригинальной функции. К счастью, Python предоставляет нам простой инструмент для обхода этой проблемы - functools.wraps
. Давайте исправим предыдущий пример, используя functools.wraps
:
Так намного лучше. Давайте двигаться дальше и знакомиться с конкретными вариантами использования декораторов.
Макет:
Примечание: @wraps
принимает на вход функцию для декорирования и добавляет функциональность копирования имени, строки документации, списка аргументов и т.д. Это открывает доступ к свойствам декорируемой функции из декоратора.
Варианты использования
Теперь давайте рассмотрим области, где декораторы действительно показывают себя и существенно упрощают работу.
Авторизация
Декораторы могут использоваться в веб-приложениях для проверки авторизации пользователя перед тем, как открывать ему доступ к функционалу. Они активно используются в веб-фреймворках Flask и Django. Вот пример проверки авторизации на декораторах:
Пример:
Журналирование
Журналирование - другая область, в которой декораторы находя широкое применение. Вот пример:
Уверен, вы уже думаете о каком-нибудь хитром использовании декораторов.
Декораторы с аргументами
Тогда подумайте вот о чем, является ли @wraps
также декоратором? Но, @wraps
же принимает аргумент, как нормальная функция. Тогда почему бы нам не сделать что-то похожее с нашими декораторами?
Когда мы используем синтаксис @my_decorator
мы применяем декорирующую функцию с декорируемой функцией в качестве параметра. Как вы помните, все в Python является объектом, в том числе и функции! Помня это, мы можем писать функции, возвращающие декорирующие функции.
Вложенные декораторы внутри функции
Давайте вернемся к нашему примеру с журналированием и напишем декоратор, который позволит нам задавать файл для сохранения логов:
Декораторы из классов
Теперь наш журналирующий декоратор находится на продакшене, однако, когда отдельные части приложения являются критичными, мы определенно хотим отзываться на возникающие ошибки как можно быстрее. Давайте предположим, что иногда мы просто хотим записывать логи в файл, а иногда мы хотим получать сообщения об ошибках по email, сохраняя логи в тоже время. Это подходящий случай для использования наследования, однако, до сих пор мы встречали только декораторы-функции.
К счастью, классы также можно использовать для создания декораторов. Давайте опробуем эту методику:
Такое решение имеет дополнительно преимущество в краткости, в сравнении с вложенными функциями, при этом синтаксис декорирования функции остается прежним:
Теперь давайте возьмем подкласс logit
и добавим функционал отправки email (эта тема не будет здесь рассмотрена):
@email_logit
будет работать также как и @logit
, при этом отправляя сообщения на почту администратору помимо журналирования.
Last updated