для чего нужен декоратор

Декораторы Python: хватит это терпеть

Всем привет! В этой статье я расскажу об инструменте, разработанном мной, который изменяет работу декораторов в Python и делает их более «Питоничными».

Я не буду рассказывать про области применения декораторов. Есть множество статей на эту тему.

Для начала, давайте вспомним: что же такое декораторы в Пайтон.

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

Как работают декораторы

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

Теперь можно декорировать:

Важно помнить!

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

Мы это увидим, если добавим две строчки в наш декоратор:

А вот и страдания: аргументы функции и аргументы декоратора

У функции, которую мы декорируем, могут быть аргументы. Принимает их вложенная функция wrapper :

А ещё, аргументы могут быть переданы непосредственно в декоратор:

Как Вам код? Вспомним, мантру Питонистов из начала статьи:

Ничего, на помощь придёт DecoratorHelper! Но, перед этим, ещё пара слов о декораторах.

Мифы декораторов

Декораторы удобны. Думаю, с этим мы уже разобрались.

Декоратор возвращает функцию. Декоратор может возвращать что угодно. Стоит лишь помнить, что если декоратор возвращает не callable объект, то вызывать его не получится.

DecoratorHelper: решение проблем

Импортируем и используем как декоратор:

Что это даёт?

Вы больше не думаете над тем, будут ли аргументы у декоратора. DecoratorHelper думает об этом вместо Вас.

Вы получаете удобный, Питоничный доступ ко всем аргументам, самой функции, к тому, что будет происходить до и после выполнения функции.

В итоге Вы получаете вместо функции объект, который имеет следующие атрибуты:

Как использовать?

Перепишем приведённый ранее код декоратора, который может принимать/не принимать аргументы:

Как мы можем видеть, тело декоратора сократилось в 8 раз. Profit!

Ограничение! Первым аргументом нельзя передавать callable объекты, иначе всё сломается 🙂 Думаю, для большинства задач, это не смертельно.

Что дальше?

В следующих версиях планируется:

Улучшенная обработка аргументов.

Встроенный счётчик вызовов.

Возможность превратить объект в синглтон.

Возможность превратить объект в буилдер.

Может быть, возможность подключить асинхронность.

И всё это в максимально удобном формате: singleton = True.

P. S. Если в комментариях будет интерес к теме, напишу вторую статью о том, как DecoratorHelper устроен. Но сразу скажу, что это уровень Junior+.

Источник

Сила и красота декораторов

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

Upd 1: изменил несколько категоричное утверждение о несходстве паттерна Декоратор и одноимённой языковой конструкции на более мягкое.

В самом начале хочется отметить, что рассматриваемый здесь декоратор (decorator) как элемент языка Python не является реализацией одноимённого паттерна проектирования, его возможности гораздо шире, хотя сам паттерн и может быть реализован через питоновский декоратор.

Что такое декоратор и простейшие способы его использования

Итак, декоратор — это удобный способ изменения поведения некоторой функции (а начиная с Python 2.6 и 3.0 и целого класса). С точки зрения синтаксиса выглядит достаточно просто. Например, следующий фрагмент кода, использующий декоратор:

Слово «эквивалентен» нужно понимать буквально: операция выполняется в момент определения функции один раз и если f1 вернёт, скажем, None, то в переменной func будет записан None. Простой пример (декорирующая функция возвращает None, в итоге func тоже оказывается равным None):

Давайте рассмотрим более практичный пример. Допустим, нужно проверить, как быстро работает некоторая функция, нужно знать, сколько времени отнимает каждый её вызов. Задача элементарно решается при помощи декоратора.

Как видно из примера, чтобы заставить функцию func при каждом исполнении печатать время работы, достаточно «обернуть» её в декоратор timer. Закомментируем строчку «@timer» и func продолжает работать как обычно.

Функция timer() является самым типичным декоратором. В качестве единственного своего параметра она принимает функцию, внутри себя создаёт новую функцию (в нашем примере с именем tmp), в которой добавляет какую-либо логику и эту самую новую функцию возвращает. Обратите внимание на сигнатуру функции tmp() — tmp(*args, **kwargs), это стандартный способ «захватить» все возможные аргументы, таким образом, наш декоратор пригоден для функций с совершенно произвольной сигнатурой.

Функцию можно обернуть в несколько декораторов. В этом случае они «выполняются» сверху вниз. Например, создадим декоратор pause(), который будет делать паузу в одну секунду перед исполнением функции.

И определим функцию func следующим образом (используя сразу два декоратора — pause и timer):

Теперь вызов func(1, 2) покажет общее время исполнения примерно одну секунду.

Более сложное использование декораторов

Вам могло показаться, что в качестве декоратора можно использовать только функцию. Это не так. В качестве декоратора может выступать любой объект, который можно «вызвать». Например, в качестве декоратора может выступать класс. Вот значительно более сложный пример, показывающий, как можно конструировать потоки (threads) при помощи декораторов:

Давайте разберём подробно этот пример. «Классический» способ создания класса потока следующий: создаётся новый класс, наследник класса threading.Thread (threading — это стандартный модуль из Питона для работы с потоками); в классе задаётся метод run(), в который помещается непосредственно код, который нужно выполнить в отдельном потоке, затем создаётся экземпляр этого класса и для него вызывается метод start(). Вот как бы это выглядело в «классическом» варианте:

В нашем же случае декорируемая функция передаётся в качестве аргумента конструктору класса потока, где присваивается компоненту класса run.

Для создания нескольких разных потоков вам нужно дважды продублировать «классический» код. А при использовании «потоковых» декораторов — только добавить вызов декоратора к функции потока.

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

В декоратор можно передавать параметры, запись вида:

По сути это означае, что декоратором является результат выполнения функции f1(123). Давайте напишем обновлённый декоратор pause(), который позволяет указывать величину паузы перед выполненением оборачиваемой функции:

Обратите внимание, как декоратор фактически создаётся динамически внутри функции pause().

Использование декораторов в классах

Использование декораторов на методах классов ничем не отличается от использования декораторов на обычных функциях. Однако для классов есть предопределённые декораторы с именами staticmethod и classmethod. Они предназначены для задания статических методов и методов класса соответственно. Вот пример их использования:

Статический метод (обёрнутый декоратором staticmethod) в принципе соответствует статическим методам в C++ или Java. А вот метод класса — это нечто более интересное. Первым аргументом такой метод получает класс (не экземпляр!), это происходит примерно так же, как с обычными методами, которые первым аргументом получают референс на экземпляр класса. В случае, когда метод класса вызывается на инстансе, первым параметром передаётся актуальный класс инстанса, это видно на примере выше: для порождённого класса передаётся именно порождённый класс.

Где ещё можно использовать декораторы
Настоящие трудности

Использовать декораторы нужно весьма осторожно, хорошо осознавая, чего именно вы хотите добиться. Излишнее их использование приводит к появлению слишком сложного для понимания кода. Можно в приступе озарения написать такое, что позже сам не разберёшься, как же написанное работает.

Использование декоратора ломает documentation strings для метода/функции. Проблему можно решить, вручную «пробрасывая» значение __doc__ в создаваемую внутри декоратора функцию. А можно воспользоваться замечательным модулем с неожиданным названием decorator, который помимо поддержки doc strings, умеет ещё множество других полезных вещей.

Источник

Готовимся к собеседованию: что такое декораторы в Python

На собеседованиях только и разговоров, что о декораторах. Разбираемся на пальцах, что это.

для чего нужен декоратор

для чего нужен декоратор

Итак, вы на собеседовании на вакансию джуна-пайтониста. Всё идёт хорошо: вы объяснили про кортежи и списки, про принципы ООП и структуры данных, даже решили небольшую задачку, и вдруг:

— Расскажите, пожалуйста, про декораторы в Python.

Простой ответ

В большинстве случаев будет достаточно сказать своими словами, что такое декоратор, и написать простейший код.

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

Функция say_hi (пример отсюда), которую мы «обернём» в декоратор, возвращает строку «всем привет». Обратите внимание: не печатает, а возвращает.

для чего нужен декоратор

С некоторых пор утверждает, что он data scientist. В предыдущих сезонах выдавал себя за математика, звукорежиссёра, радиоведущего, переводчика, писателя. Кандидат наук, но не точных. Бесстрашно пишет о Data Science и программировании на Python.

А наш декоратор превратит символы этой строки из строчных в прописные. Этой возможности у функции say_hi раньше не было, а теперь будет.

Что здесь происходит — разбираем код построчно:

Больше локальных переменных богу локальных переменных! Конечно, всё это ради понятности и читаемости.

Как это запустить? Пишем символ @, за ним название декоратора, а объявление функции say_hi переносим на строку под ней:

Источник

Ещё одна статья о декораторах в python, или немного о том, как они работают и как они могут поменять синтаксис языка

Декораторы в python являются одной из самых часто используемых возможностей языка. Множество библиотек и, особенно, веб-фреймворков предоставляют свой функционал в виде декораторов. У неопытного python разработчика уйдёт не так уж много времени, чтобы разобраться, как написать свой декоратор, благо существует огромное количество учебников и примеров, а опытный разработчик уже не раз писал свои декораторы, казалось бы, что ещё можно добавить и написать о них?

для чего нужен декораторОпределение статический метод или нет по сигнатуре, а не по декоратору

Базовое определение и простые примеры

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

app.get в примере выше регистрирует функции и связывает их с определённым путём, при этом никак не меняя их реализацию.

Однако, можно изменить поведение функции, например, добавить игнорирование исключений

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

Выглядит немного монструозно, но именно так и было принято писать декораторы с параметрами, пока кому-то в голову не пришла светлая идея, что декоратор можно создавать в виде класса.

Реализация декоратора с параметрами в виде класса

По сути нам необходимо разделить этапы создания с запоминанием переданных параметров и вызова, то есть сделать то, что делают классы, поэтому можно изначально использовать их, а не делать всё самому с двумя вложенными функциями.

Мне кажется этот код гораздо лучше читается, осталось теперь только смириться с именем класса, начинающимся с маленькой буквы или с именем декоратора, начинающимся с большой.

Декорирование классов

Декорировать можно не только функции, но и классы, например, можно реализовать декоратор, добавляющий метод преобразования класса в строку.

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

Semantic Self

Реализация декораторов из стандартной библиотеки

Например, вот так выглядит получение статического метода:

А вот так, обычного:

В случае класс методов, всё тоже довольно предсказуемо:

Примеры декораторов

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

Есть библиотеки, реализующие некоторые элементы функционального программирования: например отделение чистого кода от side эффектов, преобразование функции, генерирующей исключения. В функцию, возвращающую тип Option/Maybe:

Или алгоритм от способа его выполнения, позволяет выбирать, хотите ли вы выполнять его синхронно или асинхронно:

Источник

Всё, что нужно знать о декораторах Python

для чего нужен декоратор

Leo Matyushkin

для чего нужен декоратор

В этом руководстве по декораторам мы рассмотрим, что они собой представляют, как их создавать и использовать. По определению, декоратор – это функция, которая принимает другую функцию и расширяет поведение последней, не изменяя ее явным образом. В этом туториале мы постараемся понять, что это значит и как реализуется.

Статья является сокращенным переводом публикации Гейра Арне Хьелле Primer on Python Decorators. Оригинальный код из этой статьи доступен в GitHub-репозитории.

Так как в коде много примеров, мы также подготовили Jupyter-блокнот с текстом перевода и адаптированным кодом, чтобы его было проще запускать интерактивно (о работе с Jupyter Библиотека программиста рассказывала в статье JupyterLab и Jupyter Notebook — мощные инструменты Data Science).

1. Предварительные соображения: функции

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

1.1. Передача функции в качестве аргумента

В Python функции можно передавать и использовать в качестве аргументов, как и любой другой объект. Рассмотрим следующие три функции:

1.2. Внутренние функции

Функции, определенные внутри других функций, называются внутренними (inner functions). Пример функции с двумя внутренними функциями:

Обратите внимание, что порядок, в котором определены внутренние функции, не имеет значения. Печать происходит только при вызове функции.

Внутренние функции не определены, пока не вызвана родительская функция. То есть они локально ограничены parent() и существуют только внутри нее, как локальные переменные. При вызове функции first_child() за пределами parent() мы получим ошибку:

1.3. Возврат функций из функций

Python позволяет использовать функции в качестве возвращаемых значений. В следующем примере возвращается одна из внутренних функций внешней функции parent() :

В инструкции return возвращается ссылка на функцию, то есть имя функции указывается без скобок (иначе бы возвращался результат выполнения функции).

2. Простые декораторы 💅

2.1. Общая идея: используем знания о функциях

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

Чтобы понять, что происходит, оглянемся на предыдущие примеры. Мы просто применяем всё, что узнали до сих пор. Декорирование происходит в последней строчке:

Теперь имя say_whee указывает на внутреннюю функцию wrapper :

Добавим динамики. Рассмотрим второй пример, иллюстрирующий динамическое поведение декораторов. Сделаем так, чтобы наша функция кричала «Ура!» только в дневное время.

Декорированная функция say_whee() будет выводить «Ура» только, если она запущена в интервале c 8:00 до 22:00 (чтобы проверить разницу в поведении, «подкрутите стрелки» ⏰).

2.2. Немного синтаксического сахара! 🍭

2.3. Повторное использование декораторов

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

Теперь импортируем функцию из модуля и используем как декоратор:

Вызвав декорированную функцию, мы получаем, что оригинальная функция выполняется дважды:

2.4. Декорирование функций, принимающих аргументы 📥

Пусть у нас есть функция, принимающая аргументы. Можем ли мы ее декорировать? Попробуем:

К сожалению, запуск кода вызовет ошибку:

Проблема в том, что внутренняя функция декоратора wrapper_do_twice() не принимает аргументов. Нужно добавить их обработку. Перепишем decorators.py следующим образом:

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

2.5. Возвращение значение из декорированных функций 📤

В декораторе можно описать, что делать со значением, возвращаемым декорированной функцией:

Попытаемся использовать декорированную функцию:

Сделаем так, чтобы внутренняя функция декоратора возвращала значение декорированной функции. Поправим файл decorators.py :

Проверим, как всё работает теперь:

2.6. Интроспекция: «кто ты такой, в самом деле?» 🕵

Большое удобство в работе с Python – его способность к интроспекции. У объекта есть доступ к собственным атрибутам. К примеру, у функции можно спросить её имя и вызвать документацию:

Интроспекция работает и для пользовательских функций:

В самой декорируемой функции ничего менять не придется:

Гораздо лучше! Теперь у функции say_whee() не наступает амнезии после декорирования.

3. Несколько примеров из реального мира🎈

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

Этот блок кода является хорошим шаблоном для создания более сложных декораторов.

3.1. Декоратор для тайминга кода ⌚

Декоратор сохраняет текущее время в переменной start_time непосредственно перед запуском декорируемой функции. Это значение впоследствии вычитается из текущего значения end_time после выполнения функции. Полученная разность run_time передается в форматированную строку. Пара примеров:

3.2. Отладочный декоратор 🐞🕵

Следующий декоратор @debug будет выводить аргументы, с которыми вызвана функция, а также возвращаемое функцией значение:

Отмеченные комментариями строки соответствуют следующим операциям:

Давайте посмотрим, как декоратор работает на практике, применив его к простой функции с одним позиционным аргументов и одним аргументом, передаваемым по ключу:

Пример не сразу покажется полезным – декоратор @debug просто повторяет то, что мы ему прислали. Но он гораздо эффективнее, если его применить к небольшим вспомогательным функциям. Следующий пример иллюстрирует аппроксимацию для нахождения числавычисления числа e.

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

для чего нужен декоратор

При вызове функции approximate_e() мы увидим @debug за работой:

3.3. Замедление кода 🐌

Следующий пример вряд ли покажется полезным. Зачем нам вообще замедлять код Python? Например, мы хотим ограничить частоту, с которой функция проверяет обновление веб-ресурса. Декоратор @slow_down будет выжидать одну секунду перед запуском декорируемой функции:

Чтобы увидеть результат действия декоратора, запустите пример:

Декоратор @slow_down спит всегда лишь одну секунду. Позднее мы увидим, как передавать декоратору аргумент, чтобы контролировать его скорость.

3.4. Регистрация плагинов

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

Функция randomly_greet() случайным образом выбирает, какую из зарегистрированных функций использовать для поздравления. Удобство состоит в том, что словарь PLUGINS уже содержит ссылку для каждой функции, к которой был применен декоратор @register :

3.5. Залогинился ли пользователь? 🤔

Последний пример перед тем, как перейти к некоторым более изящным декораторам обычно используется при работе с веб-фреймворками. В этом примере мы используем Flask для настройки веб-страницы /secret – она должна быть видна только пользователям, вошедшим в систему:

4. Декораторы поинтереснее 👑

До сих пор мы видели довольно простые декораторы – нам нужно было понять, как они работают. Вы можете передохнуть и попрактиковаться в применении декораторов, чтобы позднее вернуться к этому разделу, посвященному продвинутым концепциям.

На текущий момент наш файл decorators.py имеет следующее содержание:

4.1. Декорирование классов 👨‍🏫️

Есть два способа применения декораторов к классам. Первый способ похож на то, что мы делали с функциями, – декорировать методы класса.

Давайте определим класс, в котором декорируем некоторые из методов с помощью вышеописанных декораторов @debug и @timer :

Воспользуемся классом, чтобы увидеть действие декораторов:

Другой подход – декорировать классы целиком. Написание декоратора класса очень похоже на написание декоратора функции. Разница лишь в том, что декоратор в качестве аргумента получит класс, а не функцию. Однако когда мы применяем декораторы функций к классам, их эффект может оказаться не таким, как предполагалось. В следующем примере мы применили декоратор @timer к классу:

Декорирование класса не приведет к декорированию его методов. В результате @timer измерит только время создания экземпляра класса:

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

4.2. Вложенные декораторы 🎊

К функции можно применить несколько декораторов, накладывая их действие друг на друга:

Посмотрим, что будет, если поменять порядок вызова декораторов:

4.3. Декораторы, принимающие аргументы 📬

Иногда полезно передавать декораторам аргументы, чтобы управлять их поведением. Например, @do_twice может быть расширен до декоратора @repeat(num_times ). Число повторений декорируемой функции можно было бы указать в качестве аргумента:

Подумаем, как добиться такого поведения. Обычно декоратор создает и возвращает внутреннюю функцию-обертку. Мы могли бы дополнительно «обернуть» ее поведение с помощью другой внутренней функции:

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

Давайте проверим, работает ли, как задумано:

4.4. «И того, и другого, и можно без хлеба!»

Немного потрудившись, мы можем определить декоратор, который можно использовать как с аргументами, так и без них.

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

Здесь аргумент _func действует как маркер, отмечающий, был ли декоратор вызван с аргументами или без них.

То есть в сравнении с предыдущей версией к декоратору добавилось условие if-else :

4.5. Декораторы, хранящие состояние 💾

Иногда полезно иметь декораторы, отслеживающие состояние. В качестве простого примера мы создадим декоратор, который подсчитывает, сколько раз вызывалась функция.

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

4.6. Классы в качестве декораторов функций

Обычным способом хранения состояния является использование классов. Перепишем @count_calls из предыдущего раздела, используя в качестве декоратора класс.

Кроме того, класс должен быть вызван так, чтобы он его можно было вызвать вместо декорируемой функции. Для этого в нем должен быть описан метод __call__() :

Метод __call__() вызывается всякий раз, когда мы обращаемся к экземпляру класса:

Таким, образом типичная реализация класса декоратора должна содержать __init__ () и __call__ () :

Метод __init__() должен хранить ссылку на функцию и может выполнять любую другую необходимую инициализацию.

Метод __call__() будет вызываться вместо декорированной функции. По сути, он делает то же самое, что и функция wrapper() в наших предыдущих примерах.

Декоратор @CountCalls работает так же, как и в предыдущем разделе:

5. Ещё несколько примеров из реального мира 🧭

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

5.1. Вновь замедляем код, но уже по-умному 🐢

Наша предыдущая реализация замедлителя кода @slow_down всегда «усыпляла» декорируемую функцию на одно и то же время. Давайте воспользуемся нашими знаниями о передачи в декоратор аргументов:

Проверим на примере функции countdown() :

5.2. Создание синглтонов 🗿

Оператор is возвращает True только для объектов, представляющих одну и ту же сущность.

Описанный ниже декоратор @singleton превращает класс в одноэлементный, сохраняя первый экземпляр класса в качестве атрибута. Последующие попытки создания экземпляра просто возвращают сохраненный экземпляр:

Как видите, этот декоратор класса следует тому же шаблону, что и наши декораторы функций. Единственное отличие состоит в том, что мы используем cls вместо func в качестве имени параметра.

5.3. Кэширование возвращаемых значений ⏳

Декораторы предоставляют прекрасный механизм для кэширования и мемоизации. В качестве примера давайте рассмотрим рекурсивное определение последовательности Фибоначчи:

Хотя реализация и выглядит просто, с производительностью дела обстоят плохо:

Чтобы рассчитать десятое число в последовательности Фибоначчи, в действительности достаточно лишь вычислить предыдущие числа этого ряда. Однако указанная реализация требует выполнения 177 вычислений. И ситуация быстро ухудшается: для 30-го числа потребуется 2.7 млн. операций. Это объясняется тем, что код каждый раз пересчитывает числа последовательности, уже известные из предыдущих этапов.

Обычное решение состоит в том, чтобы находить числа Фибоначчи, используя цикл for и справочную таблицу. Тем не менее, можно просто добавить к рекурсии кэширование вычислений:

Кэш работает как справочная таблица, поэтому теперь fibonacci() выполняет необходимые вычисления только один раз:

5.4. Добавление единиц измерения ⚖️

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

В следующем примере вычисляется объем цилиндра по известному радиусу и высоте, указанных в сантиметрах:

Атрибут unit можно далее использовать по мере необходимости:

Обратите внимание, что подобного поведения можно добиться, используя аннотации функций:

Однако, использовать аннотации для единиц измерения несколько затруднительно, поскольку они обычно используются для статической проверки типов.

5.5. Валидация JSON 🗝

Рассмотрим последний пример практического применения декораторов. Взглянем на следующий обработчик маршрута Flask:

Здесь мы гарантируем, что ключ student_id является частью запроса. Хотя эта проверка работает, на деле она не относится к самой функции. Кроме того, могут быть другие маршруты, которые используют ту же самую проверку. Итак, давайте абстрагируем всю стороннюю логику с помощью декоратора @validate_json :

В приведенном выше коде декоратор принимает в качестве аргумента список переменной длины. Каждый аргумент представляет собой ключ, используемый для проверки данных JSON. Функция-обертка проверяет, присутствует ли каждый ожидаемый ключ в данных JSON. Теперь обработчик маршрута может сосредоточиться на своей главной работе – обновлении оценок студентов:

Заключение

Поздравляем, вы дошли до конца статьи! 🎖️

Итак, теперь вы знаете:

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

Для ещё более глубокого погружения в декораторы, посмотрите исторический документ PEP 318, а также вики-страницу, посвященную декораторам Python.

Сторонний модуль decorator также поможет вам в создании собственных декораторов. Его документация содержит ещё больше примеров использования декораторов.

Если вам понравилась эта статья, вот ещё три родственных материала по важным темам Python:

Источник


Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *