12 Декораторов Python, которые выведут ваш код на новый уровень
Декораторы Python - это мощные инструменты, которые помогают вам создавать чистый, многоразовый и поддерживаемый код.
Я долго ждал возможности узнать об этих абстракциях, и теперь, когда у меня появилось твёрдое представление, я пишу эту статью как практическое руководство, чтобы помочь вам тоже понять концепции, лежащие в основе этих объектов.
Эта статья скорее представляет собой документированный список из 12 полезных декораторов, которые я регулярно использую в своих проектах, чтобы расширить свой код дополнительными функциональными возможностями.Мы углубимся в каждый декоратор, посмотрим на код и поэкспериментируем с некоторыми практическими примерами.
Если вы Python-разработчик, эта статья расширит ваш инструментарий полезными скриптами, чтобы повысить производительность и избежать дублирования кода.
Меньше разговоров! Я предлагаю перейти к коду прямо сейчас 💻 .
1 — @logger✏
Если вы новичок в декораторах, вы можете думать о них как о функциях, которые принимают другие функции в качестве входных данных и расширяют их функциональные возможности без изменения их основного назначения.
Давайте начнём с простого декоратора, который расширяет функцию, регистрируя время её запуска и окончания выполнения.
Результат оформления функции будет выглядеть следующим образом:
Чтобы написать этот декоратор, вам сначала нужно выбрать подходящее имя для него: давайте назовём его logger.
logger - это функция, которая принимает функцию в качестве входных данных и возвращает другую функцию в качестве выходных данных. Функция вывода обычно представляет собой расширенную версию функции ввода. В нашем случае мы хотим, чтобы функция вывода окружала вызов функции ввода операторами start и end.
Поскольку мы не знаем, какие аргументы использует функция ввода, мы можем передать их из функции wrapper, используя *args и **kwargs. Эти выражения позволяют передавать произвольное количество позиционных аргументов и аргументов ключевого слова.
Вот простая реализация декоратора logger:
Теперь вы можете применить logger к some_function или любой другой функции, если уж на то пошло.
Python предоставляет для этого уникальный синтаксис (использование символа @).
2 — @wraps 🎁
Этот декоратор обновляет функцию wrapper, чтобы она выглядела как исходная функция, и наследует её имя и свойства.
Чтобы понять, что делает @wraps и почему вы должны его использовать, давайте возьмём предыдущий декоратор и применим его к простой функции, которая добавляет два числа.
(Этот декоратор еще не использует @wraps):
Если мы проверим имя и документацию оформленной функции add_two_numbers, вызвав атрибуты __name__ и __doc__, мы получим … неестественные (и всё же ожидаемые) результаты:
Вместо этого мы получаем название оболочки и документацию ⚠
Это нежелательный результат. Мы хотим сохранить оригинальное название функции и документацию. Вот когда пригодится декоратор @wraps.
Всё, что вам нужно сделать, это использовать данный декоратор в функции wrapper.
Перепроверив название и документацию, мы видим метаданные исходной функции:
3 — @lru_cache 💨
Это встроенный декоратор, который вы можете импортировать из functools .
Он кэширует возвращаемые значения функции, используя алгоритм кэширования (LRU) для удаления наименее используемых значений, когда кэш заполнен.
Обычно я использую этот декоратор для длительных задач, которые не изменяют выходные данные при одних и тех же входных данных, таких как запрос к базе данных, запрос статической удалённой веб-страницы или выполнение какой-либо интенсивной обработки.
В следующем примере я использую lru_cache для оформления функции, которая имитирует некоторую обработку. Затем я применяю функцию к одному и тому же входному сигналу несколько раз подряд.
Если бы вы захотели самостоятельно реализовать декоратор кэша с нуля, вот как это можно было бы сделать:
- Вы добавляете пустой словарь в качестве атрибута к функции-оболочке для хранения ранее вычисленных значений функцией ввода
- При вызове функции ввода вы сначала проверяете, присутствуют ли её аргументы в кэше. Если это так, верните результат. В противном случае вычислите его и поместите в кэш.
4 — @repeat 🔁
Этот декоратор реализует вызов функции несколько раз подряд.
Это может быть полезно для целей отладки, стресс-тестов или автоматизации повторения нескольких задач.
В отличие от предыдущих декораторов, этот ожидает ввода параметра.
Следующий пример определяет декоратор с именем repeat, который принимает количественное число в качестве аргумента. Затем декоратор определяет функцию, называемую wrapper, которая оборачивается вокруг оформляемой функции. wrapper вызывает оформленную функцию столько раз, сколько было указано в аргументе.
5 — @timeit ⏲
Этот декоратор измеряет время выполнения функции и выводит результат: он служит для отладки или мониторинга.
В следующем фрагменте декоратор timeit измеряет время, необходимое для выполнения функции process_data, и выводит прошедшее время в секундах.
6 — @retry 🔁
Этот декоратор заставляет функцию повторять попытку несколько раз, когда она сталкивается с исключением.
Он принимает три аргумента: количество повторных попыток, исключение для перехвата и повторной попытки и время ожидания между повторными попытками.
Он работает следующим образом:
- Функция wrapper запускает цикл for итераций num_retries.
- На каждой итерации он вызывает функцию ввода в блоке try/except. Когда вызов выполняется успешно, он прерывает цикл и возвращает результат. В противном случае он переходит в спящий режим на время sleep_time и переходит к следующей итерации.
- Когда вызов функции не выполняется успешно после завершения цикла for, функция wrapper вызывает исключение.
7 — @countcall 🔢
Декоратор @countcall подсчитывает, сколько раз была вызвана функция.
Это число сохраняется в атрибуте count .
8 — @rate_limited 🚧
Это декоратор, который ограничивает скорость, с которой функция может быть вызвана, путём перехода в режим ожидания, если функция вызывается слишком часто.
соответствующего промежутка времени, если это необходимо, чтобы убедиться, что ограничение скорости не превышено. Время ожидания вычисляется как min_interval - elapsed, где min_interval - минимальный интервал времени (в секундах) между двумя вызовами функции, а elapsed - время, прошедшее с момента последнего вызова.
Если прошедшее время меньше минимального интервала, функция ожидает left_to_wait секунд перед повторным выполнением.
Следовательно, эта функция приводит к небольшим временным затратам между вызовами, но гарантирует, что ограничение скорости не будет превышено.
Существует также сторонний пакет, который реализует ограничение скорости API: он называется ratelimit.
Чтобы использовать этот пакет, просто используйте любой декоратор, который выполняет вызов API:
Если оформленная функция вызывается больше раз, чем разрешено, возникает исключение ratelimit.RateLimitException.
Чтобы иметь возможность обрабатывать это исключение, вы можете использовать декоратор sleep_and_retry в сочетании с декоратором ratelimit.
Это приводит к тому, что функция переходит в спящий режим оставшееся количество времени, прежде чем будет выполнена снова.
9 — @dataclass 🗂
Декоратор @dataclass в Python используется для оформления классов.
Он автоматически генерирует специальные методы, такие как __init__, __repr__, __eq__, __lt__ и __str__ для классов, которые в основном хранят данные. Это может сократить объём шаблонного кода и сделать классы более удобочитаемыми и ремонтопригодными.
Он также предоставляет готовые методы для красивого представления объектов, преобразования их в формат JSON, придания им неизменяемости и т.д.
Декоратор @dataclass был представлен в Python 3.7 и доступен в стандартной библиотеке.
10 — @register 🛑
Если ваш скрипт на Python случайно завершается, а вы всё ещё хотите выполнить некоторые задачи, чтобы сохранить свою работу, выполнить очистку или вывести сообщение, я нахожу, что декоратор register довольно удобен в этом контексте.
При запуске этого скрипта и нажатии CTRL+C,
мы видим вывод функции terminate.
11 — @property 🏠
Декоратор property используется для определения свойств класса, которые являются методами получения, установки и удаления атрибута экземпляра класса.
Используя данный декоратор, вы можете определить метод как свойство класса и получить к нему доступ, как если бы это был атрибут класса, без явного вызова метода.
Это полезно, если вы хотите добавить некоторые ограничения и логику проверки вокруг получения и установки значения.
В следующем примере мы определяем параметр для свойства rating, чтобы применить ограничение к входным данным (от 0 до 5).
12 — @singledispatch
Этот декоратор позволяет функции иметь разные реализации для разных типов аргументов.
Заключение
Декораторы - это полезные абстракции для расширения вашего кода дополнительными функциями, такими как кэширование, автоматическое повторение попыток, ограничение скорости, логирование или превращение ваших классов в контейнеры данных.
Однако на этом дело не заканчивается, поскольку вы можете проявить больше креативности и внедрить свои собственные декораторы для решения очень специфических задач.
Вот список потрясающих декораторов, из которых можно черпать вдохновение.
Спасибо за чтение!
Ссылки на используемую литературу
Статья была взята из этого источника: