12 декораторов Python, которые выведут ваш код на новый уровень
Декораторы Python - это мощные инструменты, которые помогают вам создавать чистый, многоразовый и поддерживаемый код.
Я долго ждал возможности узнать об этих инструментах, и теперь, когда я приобрел твёрдое представление, я пишу эту статью как практическое руководство, чтобы помочь вам понять концепции, лежащие в основе этих объектов.
Эта статья представляет собой документированный список из 12 полезных декораторов, которые я регулярно использую в своих проектах, чтобы расширить свой код дополнительными функциональными возможностями.Мы углубимся в каждый декоратор, посмотрим на код и поэкспериментируем с некоторыми практическими примерами.
Если вы разработчик на Python, этот пост расширит ваш инструментарий полезными скриптами, чтобы повысить производительность и избежать дублирования кода.
1 — @logger (простой декоратор для начинающих)
Если вы новичок в декораторах, вы можете думать о них как о функциях, которые принимают другие функции в качестве входных данных и расширяют их возможности без изменения их основного назначения.
Давайте начнём с простого декоратора, который расширяет функцию, регистрируя время её запуска и окончания выполнения.
Результат оформления функции будет выглядеть следующим образом:
Чтобы написать этот декоратор, вам сначала нужно выбрать подходящее имя: давайте назовём его logger.
Logger- это функция, которая принимает другую функцию в качестве входных данных и возвращает функцию в качестве выходных данных. Функция вывода обычно представляет собой расширенную версию функции ввода. В нашем случае мы хотим, чтобы функция вывода поддерживала вызов функции ввода операторами start и end.
Поскольку мы не знаем, какие аргументы использует функция ввода, мы можем передать их из функции-оболочки, используя *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 для оформления функции, которая имитирует некоторую обработку. Затем я применяю функцию к одному и тому же входному сигналу несколько раз подряд:
Если вы хотите самостоятельно реализовать декоратор кэша с нуля, вот как это можно сделать:
- Вы добавляете пустой словарь в качестве атрибута к функции wrapper для хранения ранее вычисленных функцией ввода значений
- При вызове функции ввода вы сначала проверяете, присутствуют ли её аргументы в кэше. Если это так, возвращайте результат. В противном случае вычислите его и поместите в кэш.
4 — @repeat
Этот декоратор запускает вызов функции несколько раз подряд.
Это может быть полезно для целей отладки, стресс-тестирования или автоматизации повторения нескольких задач.
В отличие от предыдущих декораторов, этот ожидает ввода параметра.
Следующий пример определяет декоратор с именем repeat, который принимает некоторое количество повторений в качестве аргумента. Затем декоратор определяет функцию, называемую wrapper, которая оборачивается вокруг оформляемой функции. Функция wrapper вызывает оформленную функцию количество раз, равное указанному в аргументе числу:
5 — @timeit
Этот декоратор измеряет время выполнения функции и выводит результат: он служит для отладки или мониторинга.
В следующем фрагменте декоратор timeit измеряет время, необходимое для выполнения функции process_data, и выводит его в секундах:
6 — @retry
Этот декоратор заставляет функцию повторять попытку несколько раз, когда она сталкивается с исключением.
Он принимает три аргумента: количество повторных попыток, исключение для перехвата и время ожидания между повторными попытками.
Это работает следующим образом:
- Функция wrapper запускает цикл for итераций num_retries.
- На каждой итерации он вызывает функцию ввода в блоке try/except. Когда вызов выполняется успешно, он прерывает цикл и возвращает результат. В противном случае он переходит в спящий режим на секунды sleep_time и переходит к следующей итерации.
- Когда вызов функции не выполняется успешно после завершения цикла for, функция wrapper вызывает исключение.
7 — @countcall
Этот декоратор подсчитывает, сколько раз была вызвана функция.
Число, являющееся результатом, сохраняется в атрибуте count .
8 — @rate_limited
Это декоратор, который ограничивает скорость, с которой функция может быть вызвана, путём перехода в режим ожидания, если функция вызывается слишком часто.
Декоратор работает, измеряя время, прошедшее с момента последнего вызова функции, и ожидая соответствующего промежутка времени, если это необходимо, чтобы убедиться, что ограничение скорости не превышено. Время ожидания вычисляется как min_interval - elapsed, где min_interval - минимальный интервал времени (в секундах) между двумя вызовами функции, а elapsed - время, прошедшее с момента последнего вызова.
Если прошедшее время меньше минимального интервала, функция ожидает left_to_wait секунд перед повторным выполнением.
Следовательно, эта функция приводит к небольшим временным затратам между вызовами, но гарантирует, что ограничение скорости не будет превышено.
Чтобы использовать этот пакет, просто оформите какую-либо функцию, которая выполняет вызов API:
Если оформленная функция вызывается больше раз, чем разрешено, возникает исключение ratelimit.RateLimitException.
Чтобы иметь возможность обрабатывать это исключение, вы можете использовать декоратор sleep_and_retry в сочетании с декоратором theratelimit.
Это приводит к тому, что функция переходит в спящий режим оставшееся количество времени, прежде чем будет выполнена снова.
9 — @dataclass
Декоратор @dataclass в Python используется для оформления классов.
Он автоматически генерирует специальные методы, такие как __init__, __repr__, __eq__, __lt__ и __str__ для классов, которые хранят данные. Это может сократить объём шаблонного кода и сделать классы более удобочитаемыми и ремонтопригодными.
Он также предоставляет готовые методы для красивого представления объектов, преобразования их в формат JSON, придания им неизменяемости и т.д.
Декоратор @dataclass был представлен в Python 3.7 и доступен в стандартной библиотеке.
10 — @register
Если ваш скрипт на Python случайно завершается, а вы всё ещё хотите выполнить некоторые задачи, чтобы сохранить свою работу, выполнить очистку или вывести сообщение, я нахожу, что декоратор @register будет довольно удобен в использовании.
При запуске этого скрипта и нажатии CTRL+C, мы видим вывод функцию terminate.
11 — @property
Декоратор property используется для определения свойств класса, которые являются методами getter, setter и deleter атрибута экземпляра класса.
Используя этот декоратор, вы можете определить метод как класс property и получить к нему доступ, как если бы это был атрибут класса, без явного вызова метода.
Это полезно, если вы хотите добавить некоторые ограничения и логику проверки вокруг получения и установки значения.
В следующем примере мы определяем параметр для свойства rating, чтобы применить ограничение к входным данным (от 0 до 5):
12 — @singledispatch
Этот декоратор позволяет функции иметь разные реализации для разных типов аргументов.
Заключение
Декораторы - это полезные абстракции для расширения вашего кода дополнительными функциями, такими как кэширование, автоматическое повторение попыток, ограничение скорости, логирование или превращение ваших классов в контейнеры данных.
Однако на этом дело не заканчивается, поскольку вы можете проявить больше креативности и внедрить свои собственные декораторы для решения очень специфических задач.
Спасибо за чтение!
Ресурсы
Статья была взята из этого источника: