Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор

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

Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор. Смотреть фото Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор. Смотреть картинку Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор. Картинка про Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор. Фото Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор

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

Итак, что же такое «декоратор»?

Впереди достаточно длинная статья, так что, если кто-то спешит — вот пример того, как работают декораторы:

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

Функции в Python’e являются объектами

Для того, чтобы понять, как работают декораторы, в первую очередь следует осознать, что в Python’е функции — это тоже объекты.
Давайте посмотрим, что из этого следует:

Запомним этот факт, скоро мы к нему вернёмся, но кроме того, стоит понимать, что функция в Python’e может быть определена… внутри другой функции!

Ссылки на функции

Ну что, вы всё ещё здесь?:)

Подождите, раз мы можем возвращать функцию, значит, мы можем и передавать её другой функции, как параметр:

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

Создадим свой декоратор «вручную»

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

Вы ведь уже догадались, что это ровно тоже самое, что делают @декораторы.:)

Разрушаем ореол таинственности вокруг декораторов

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

Да, всё действительно так просто! decorator — просто синтаксический сахар для конструкций вида:

Декораторы — это просто pythonic-реализация паттерна проектирования «Декоратор». В Python включены некоторые классические паттерны проектирования, такие как рассматриваемые в этой статье декораторы, или привычные любому пайтонисту итераторы.

Конечно, можно вкладывать декораторы друг в друга, например так:

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

Следует помнить о том, что порядок декорирования ВАЖЕН:

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

Источник

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Что это даёт?

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

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

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

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

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

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

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

Что дальше?

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

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

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

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

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

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

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

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

Источник

Декораторы в Python: понять и полюбить

Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор. Смотреть фото Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор. Смотреть картинку Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор. Картинка про Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор. Фото Что такое декоратор как выглядит декоратор в питоне как написать мемоизирующий декоратор

Декораторы — один из самых полезных инструментов в Python, однако новичкам они могут показаться непонятными. Возможно, вы уже встречались с ними, например, при работе с Flask, но не хотели особо вникать в суть их работы. Эта статья поможет вам понять, чем являются декораторы и как они работают.

Что такое декоратор?

Новичкам декораторы могут показаться неудобными и непонятными, потому что они выходят за рамки «обычного» процедурного программирования как в Си, где вы объявляете функции, содержащие блоки кода, и вызываете их. То же касается и объектно-ориентированного программирования, где вы определяете классы и создаёте на их основе объекты. Декораторы не принадлежат ни одной из этих парадигм и исходят из области функционального программирования. Однако не будем забегать вперёд, разберёмся со всем по порядку.

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

Как работают функции

Все мы знаем, что такое функции, не так ли? Не будьте столь уверены в этом. У функций Python есть определённые аспекты, с которыми мы нечасто имеем дело, и, как следствие, они забываются. Давайте проясним, что такое функции и как они представлены в Python.

Функции как процедуры

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

Функции как объекты первого класса

В Python всё является объектом, а не только объекты, которые вы создаёте из классов. В этом смысле он (Python) полностью соответствует идеям объектно-ориентированного программирования. Это значит, что в Python всё это — объекты:

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

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

И тут в дело вступает функциональное программирование, а вместе с ним — декораторы.

Функциональное программирование — функции высших порядков

В Python используются некоторые концепции из функциональных языков вроде Haskell и OCaml. Пропустим формальное определение функционального языка и перейдём к двум его характеристикам, свойственным Python:

Функциональному программированию присущи и другие свойства вроде отсутствия побочных эффектов, но мы здесь не за этим. Лучше сконцентрируемся на другом — функциях высших порядков. Что есть функция высшего порядка? Снова обратимся к Википедии:

Функции высших порядков — это такие функции, которые могут принимать в качестве аргументов и возвращать другие функции.

Если вы знакомы с основами высшей математики, то вы уже знаете некоторые математические функции высших порядков порядка вроде дифференциального оператора d/dx. Он принимает на входе функцию и возвращает другую функцию, производную от исходной. Функции высших порядков в программировании работают точно так же — они либо принимают функцию(и) на входе и/или возвращают функцию(и).

Пара примеров

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

Здесь мы определили простую функцию. Из фрагмента кода далее вы увидите, что эта функция, как и классы с числами, является объектом в Python:

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

Мы можем хранить функции в переменных:

Определять функции внутри других функций:

Передавать функции в качестве аргументов и возвращать их из других функций:

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

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

Повторим определение декоратора:

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

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

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

Иными словами, выражение @decorator_function вызывает decorator_function() с hello_world в качестве аргумента и присваивает имени hello_world возвращаемую функцию.

И хотя этот декоратор мог вызвать вау-эффект, он не очень полезный. Давайте взглянем на другие, более полезные (наверное):

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

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

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

Используем аргументы и возвращаем значения

В приведённых выше примерах декораторы ничего не принимали и не возвращали. Модифицируем наш декоратор для измерения времени выполнения:

Вывод после выполнения:

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

Декораторы с аргументами

Мы также можем создавать декораторы, которые принимают аргументы. Посмотрим на пример:

Здесь мы модифицировали наш старый декоратор таким образом, чтобы он выполнял декорируемую функцию iters раз, а затем выводил среднее время выполнения. Однако чтобы добиться этого, пришлось воспользоваться природой функций в Python.

Да, это может быть действительно сложно уместить в голове, поэтому держите правило:

Декоратор принимает функцию в качестве аргумента и возвращает функцию.

Объекты-декораторы

Напоследок стоит упомянуть, что не только функции, а любые вызываемые объекты могут быть декоратором. Экземпляры классов/объекты с методом __call__() тоже можно вызывать, поэтому их можно использовать в качестве декораторов. Эту функциональность можно использовать для создания декораторов, хранящих какое-то состояние. Например, вот декоратор для мемоизации:

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

Заключение

Надеемся, эта статья помогла вам понять, какая «магия» лежит в основе работы декораторов.

Источник

Глубокое погружение в декораторы Python

Обзор

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

Прежде чем перейти к некоторым классным примерам, если вы хотите изучить происхождение декораторов немного больше, тогда функции декораторов появились сначала в Python 2.4. См. PEP-0318 для интересного обсуждения истории, обоснования и выбора имени «декоратор». Декораторы класса появились сначала в Python 3.0. см. PEP-3129, который довольно короткий и построен поверх всех концепций и идей декораторов функций.

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

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

Классическими примерами являются встроенные декораторы @staticmethod и @classmethod. Эти декораторы превращают метод класса в соответствие со статическим методом (не предоставляется первый аргумент self) или метод класса (первым аргументом является класс, а не экземпляр).

Классические декораторы

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

Мемоизации

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

Это может стать огромным ускорителем производительности для функций, которые выполняют дорогостоящую обработку (например, обращаются к удаленной базе данных или вызывают несколько API REST) и часто вызываются с теми же параметрами.

Контрактное программирование

Как насчет пары декораторов, называемых @precondition и @postcondition для проверки входного аргумента, а также результата? Рассмотрим следующую простую функцию:

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

Наша приятная однострочная функция add_small_ints() просто стала неприятной трясиной с уродливыми утверждениями. В реальной функции очень сложно сразу увидеть, что она на самом деле делает. С декораторами условия pre и post могут перемещаться из тела функции:

Авторизация

Предположим, у вас есть класс, требующий авторизации через секрет для всех его многочисленных методов. Будучи непревзойденным разработчиком Python, вы, вероятно, предпочтете @authorized decorator:

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

Более критически, если кто-то добавляет новый метод и забывает добавить декоратор @authorized, то у вас будут проблемы с безопасностью. У декораторов класса Python 3 есть страховка. Следующий синтаксис позволит вам (с правильным определением декоратора класса) автоматически разрешить каждый метод целевых классов:

Все, что вам нужно сделать, это задекорировать сам класс. Обратите внимание, что декоратор может быть умным и игнорировать специальный метод, например __init __(), или может быть настроен для применения к определенному подмножеству, если это необходимо. Здесь ваша фантазия не ограничена.

Другие примеры

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

Что такое декоратор?

Теперь, когда вы видели несколько примеров в действии, пришло время открыть магию. Формальное определение состоит в том, что декоратор является вызываемым кодом, который принимает callable (цель) и возвращает callable (декоратор), который принимает те же аргументы, что и исходная цель.

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

Декораторы функций

Декоратором функции является декоратор, который используется для декорирования функции или метода. Предположим, мы хотим напечатать строку «Да, это работает!» каждый раз, когда вызывается декорированная функция или метод, прежде чем вызывать подлинную функцию. Вот способ без декоратора для достижения этого. Вот функция foo(), которая печатает «foo() здесь»:

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

С этим подходом существует несколько проблем:

Декоратор, который выполняет один и тот же результат, а также многоразовый и составной, выглядит так:

Обратите внимание, что yeah_it_works() является функцией (следовательно, callable), которая принимает вызываемый **f** в качестве аргумента и возвращает callable (вложенную функцию **decorated**), которая принимает любое число и типы аргументов.

Теперь мы можем применить его к любой функции:

Как это работает? Исходные функции f1, f2 и f3 были заменены декорированной вложенной функцией, возвращаемой yeah_it_works. Для каждой отдельной функции захваченная f является исходной функцией (f1, f2 или f3), поэтому декорированная функция отличается и делает правильную вещь, печатается «Да, это работает!». а затем вызывает исходную функцию f.

Декораторы классов

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

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

Декоратор класса, который достигает этого же результата:

Заключение

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

Источник

Декораторы

Декораторы в Python и примеры их практического использования.

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

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

Создадим свой декоратор «вручную»:

Наверное, теперь мы бы хотели, чтобы каждый раз, во время вызова stand_alone_function, вместо неё вызывалась stand_alone_function_decorated. Для этого просто перезапишем stand_alone_function:

Собственно, это и есть декораторы. Вот так можно было записать предыдущий пример, используя синтаксис декораторов:

То есть, декораторы в python — это просто синтаксический сахар для конструкций вида:

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

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

Также нужно помнить о том, что важен порядок декорирования. Сравните с предыдущим примером:

Передача декоратором аргументов в функцию

Однако, все декораторы, которые мы рассматривали, не имели одного очень важного функционала — передачи аргументов декорируемой функции. Собственно, это тоже несложно сделать.

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

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

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

Декораторы с аргументами

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

Теперь перепишем данный код с помощью декораторов:

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

Таким образом, мы можем передавать декоратору любые аргументы, как обычной функции. Мы можем использовать и распаковку через *args и **kwargs в случае необходимости.

Некоторые особенности работы с декораторами

Последняя проблема частично решена добавлением в модуле functools функции functools.wraps, копирующей всю информацию об оборачиваемой функции (её имя, из какого она модуля, её документацию и т.п.) в функцию-обёртку.

Забавным фактом является то, что functools.wraps тоже является декоратором.

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

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

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

Источник

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

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