Проверьте документацию, чтобы увидеть, как работают декораторы. Вот что вы просили:
from functools import wraps
def makebold(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return "<b>" + fn(*args, **kwargs) + "</b>"
return wrapped
def makeitalic(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return "<i>" + fn(*args, **kwargs) + "</i>"
return wrapped
@makebold
@makeitalic
def hello():
return "hello world"
@makebold
@makeitalic
def log(s):
return s
print hello() # returns "<b><i>hello world</i></b>"
print hello.__name__ # with functools.wraps() this returns "hello"
print log('hello') # returns "<b><i>hello</i></b>"
Паоло Бергантино
источник
__name__
и, говоря о пакете декоратора, сигнатуру функции).*args
и**kwargs
должен быть добавлен в ответ. Декорированная функция может иметь аргументы, и они будут потеряны, если не указано иное.*args
,**kwargs
. Простой способ решить эти 3 проблемы одновременно - использовать,decopatch
как описано здесь . Вы также можете использовать,decorator
как уже упоминалось Мариус Гедминас, для решения пунктов 2 и 3.Если у вас нет длинных объяснений, посмотрите ответ Паоло Бергантино .
Основы декоратора
Функции Python являются объектами
Чтобы понять декораторы, вы должны сначала понять, что функции - это объекты в Python. Это имеет важные последствия. Давайте посмотрим, почему на простом примере:
Имейте это в виду. Мы скоро вернемся к этому.
Еще одно интересное свойство функций Python - они могут быть определены внутри другой функции!
Ссылки на функции
Хорошо, все еще здесь? Теперь самое интересное ...
Вы видели, что функции являются объектами. Следовательно, функции:
Это означает, что функция может быть
return
другой функцией .Есть больше!
Если вы можете
return
использовать функцию, вы можете передать ее в качестве параметра:Ну, у вас просто есть все необходимое для понимания декораторов. Видите ли, декораторы являются «обертками», что означает, что они позволяют вам выполнять код до и после функции, которую они декорируют, без изменения самой функции.
Декораторы ручной работы
Как бы вы сделали это вручную:
Теперь вы, вероятно, хотите, чтобы каждый раз, когда вы звоните
a_stand_alone_function
,a_stand_alone_function_decorated
вызывается вместо. Это просто, просто переписатьa_stand_alone_function
с помощью функции, возвращаемойmy_shiny_new_decorator
:Декораторы демистифицированы
Предыдущий пример с использованием синтаксиса декоратора:
Да, это все, это так просто.
@decorator
это просто ярлык для:Декораторы - это всего лишь питонический вариант шаблона дизайна декоратора . Существует несколько классических шаблонов проектирования, встроенных в Python для упрощения разработки (например, итераторы).
Конечно, вы можете накапливать декораторы:
Использование синтаксиса декоратора Python:
Порядок установки декораторами ДЕЛА:
Теперь: чтобы ответить на вопрос ...
В заключение вы можете легко увидеть, как ответить на вопрос:
Теперь вы можете просто оставить себя довольным или сжечь свой мозг немного больше и увидеть расширенные возможности использования декораторов.
Вывод декораторов на новый уровень
Передача аргументов в декорированную функцию
Методы декорирования
Отличная особенность Python в том, что методы и функции действительно одинаковы. Единственное отличие состоит в том, что методы ожидают, что их первый аргумент является ссылкой на текущий объект (
self
).Это означает, что вы можете создать декоратор для методов таким же образом! Просто не забудьте принять
self
во внимание:Если вы создаете декоратор общего назначения - тот, который вы примените к любой функции или методу, независимо от его аргументов, - тогда просто используйте
*args, **kwargs
:Передача аргументов декоратору
Отлично, что бы вы сказали о передаче аргументов самому декоратору?
Это может быть несколько искажено, так как декоратор должен принимать функцию в качестве аргумента. Следовательно, вы не можете передавать аргументы декорированной функции напрямую декоратору.
Прежде чем торопиться с решением, напишем небольшое напоминание:
Это точно так же. "
my_decorator
называется". Поэтому, когда@my_decorator
вы говорите Python, чтобы он вызывал функцию, помеченную переменной "my_decorator
" ".Это важно! Ярлык, который вы даете, может указывать непосредственно на декоратора - или нет .
Давайте получим зло. ☺
Здесь нет ничего удивительного.
Давайте сделаем точно то же самое, но пропустим все надоедливые промежуточные переменные:
Давайте сделаем это еще короче :
Эй, ты видел это? Мы использовали вызов функции с
@
синтаксисом " "! :-)Итак, вернемся к декораторам с аргументами. Если мы можем использовать функции для генерации декоратора на лету, мы можем передать аргументы этой функции, верно?
Вот оно: декоратор с аргументами. Аргументы могут быть установлены как переменные:
Как видите, вы можете передавать аргументы декоратору как любую функцию, используя этот трюк. Вы даже можете использовать,
*args, **kwargs
если хотите. Но помните, декораторы называются только один раз . Просто когда Python импортирует скрипт. Вы не можете динамически устанавливать аргументы впоследствии. Когда вы выполняете «import x», функция уже оформлена , поэтому вы ничего не можете изменить.Давайте потренируемся: украшать декоратор
Хорошо, в качестве бонуса я дам вам фрагмент кода, чтобы заставить любого декоратора в целом принять любой аргумент. В конце концов, чтобы принимать аргументы, мы создали наш декоратор, используя другую функцию.
Мы завернули декоратор.
Что-нибудь еще, что мы недавно видели, что завернутая функция?
О да, декораторы!
Давайте немного повеселимся и напишем декоратор для декораторов:
Может использоваться следующим образом:
Я знаю, в прошлый раз, когда у вас было это чувство, это было после того, как вы услышали, как парень сказал: «прежде чем понимать рекурсию, вы должны сначала понять рекурсию». Но разве тебе не нравится овладевать этим?
Лучшие практики: декораторы
functools
Модуль был введен в Python 2.5. Он включает в себя функциюfunctools.wraps()
, которая копирует имя, модуль и строку документации оформленной функции в свою оболочку.(Интересный факт:
functools.wraps()
это декоратор! ☺)Чем могут быть полезны декораторы?
Теперь большой вопрос: для чего я могу использовать декораторы?
Кажется круто и мощно, но практический пример был бы великолепен. Ну, есть 1000 возможностей. Классическое использование расширяет поведение функции из внешней библиотеки (вы не можете ее изменить) или для отладки (вы не хотите изменять ее, потому что она временная).
Вы можете использовать их для расширения нескольких функций в режиме СУХОГО, например, так:
Конечно, хорошая вещь с декораторами в том, что вы можете использовать их практически сразу, без переписывания. СУХОЙ, я сказал:
Сам Python предоставляет несколько декораторов:
property
,staticmethod
и т.д.Это действительно большая игровая площадка.
источник
__closure__
атрибут), чтобы извлечь исходную недекорированную функцию. Один пример использования документирован в этом ответе, который охватывает, как можно внедрить функцию декоратора на более низком уровне в ограниченных обстоятельствах.@decorator
Синтаксис Python, вероятно, чаще всего используется для замены функции закрытием оболочки (как описано в ответе). Но он также может заменить функцию чем-то другим. Встроенная командаproperty
,classmethod
иstaticmethod
декораторы заменить функцию с дескриптором, например. Декоратор также может сделать что-то с функцией, например, сохранить ссылку на нее в каком-либо реестре, а затем вернуть ее без изменений, без какой-либо оболочки.В качестве альтернативы, вы можете написать фабричную функцию, которая возвращает декоратор, который оборачивает возвращаемое значение декорированной функции в тег, передаваемый фабричной функции. Например:
Это позволяет вам написать:
или
Лично я бы написал декоратору несколько иначе:
что даст:
Не забудьте конструкцию, для которой синтаксис декоратора является сокращением:
источник
def wrap_in_tag(*kwargs)
тогда@wrap_in_tag('b','i')
Похоже, другие люди уже сказали вам, как решить проблему. Я надеюсь, что это поможет вам понять, что такое декораторы.
Декораторы просто синтаксический сахар.
Эта
расширяется до
источник
@decorator()
(а не@decorator
) это синтаксический сахар дляfunc = decorator()(func)
. Это также обычная практика, когда нужно создавать декораторы «на лету»И, конечно же, вы можете вернуть лямбды из функции декоратора:
источник
makebold = lambda f : lambda "<b>" + f() + "</b>"
makebold = lambda f: lambda: "<b>" + f() + "</b>"
makebold = lambda f: lambda *a, **k: "<b>" + f(*a, **k) + "</b>"
functools.wraps
для того, чтобы не выбросить строку документации / подпись / имяsay
@wraps
другом месте на этой странице , не собирается , чтобы помочь мне , когда я печатаюhelp(say)
и получить «Помощь на функцию <лямбда>` вместо „Справка о функции говорят“ .Декораторы Python добавляют дополнительную функциональность к другой функции
Курсив декоратор может быть как
Обратите внимание, что функция определена внутри функции. Что он в основном делает, так это заменяет функцию на новую. Например, у меня есть этот класс
Теперь, скажем, я хочу, чтобы обе функции печатали «---» после и до того, как они будут выполнены. Я мог бы добавить печать «---» до и после каждого оператора печати. Но поскольку я не люблю повторяться, я сделаю декоратор
Так что теперь я могу изменить свой класс на
Для получения дополнительной информации о декораторах, посетите http://www.ibm.com/developerworks/linux/library/l-cpdecor.html.
источник
self
Аргумент необходим, потому чтоnewFunction()
определенный вaddDashes()
был специально разработан, чтобы быть декоратором метода, а не обычным декоратором функции.self
Аргумент представляет собой экземпляр класса и передается методы класса , используют ли они это или нет - смотрите раздел под названием Украшать методы в ответ @ E-Satis в.functools.wraps
Вы можете сделать два отдельных декоратора, которые будут делать то, что вы хотите, как показано ниже. Обратите внимание на использование
*args, **kwargs
в объявленииwrapped()
функции, которая поддерживает декорированную функцию, имеющую несколько аргументов (что на самом деле не обязательно для примераsay()
функции, но включено для универсальности).По тем же причинам
functools.wraps
декоратор используется для изменения мета-атрибутов обернутой функции, чтобы они соответствовали атрибутам декорируемой функции. Это делает сообщения об ошибках и документацию встроенной функции (func.__doc__
) теми же, что и у оформленной функции, а неwrapped()
с.Уточнения
Как вы можете видеть, в этих двух декораторах много повторяющегося кода. Учитывая это сходство, было бы лучше вместо этого создать общий, который на самом деле был фабрикой декораторов - другими словами, функцию декоратора, которая создает другие декораторы. Таким образом, будет меньше повторения кода и будет соблюдаться принцип DRY .
Чтобы сделать код более читабельным, вы можете назначить более описательное имя сгенерированным на заводе декоратором:
или даже объединить их так:
КПД
Хотя вышеприведенные примеры действительно работают, сгенерированный код требует значительных затрат в виде вызовов посторонних функций, когда одновременно применяется несколько декораторов. Это может не иметь значения, в зависимости от точного использования (которое может быть связано с вводом / выводом, например).
Если важна скорость декорированной функции, накладные расходы могут быть сведены к одному дополнительному вызову функции путем написания немного другой фабричной функции декоратора, которая реализует добавление всех тегов одновременно, так что она может генерировать код, который позволяет избежать дополнительных вызовов функций используя отдельные декораторы для каждого тега.
Для этого требуется больше кода в самом декораторе, но он выполняется только тогда, когда он применяется к определениям функций, а не позже, когда они сами вызываются. Это также применяется при создании более удобочитаемых имен с использованием
lambda
функций, как показано ранее. Образец:источник
Еще один способ сделать то же самое:
Или, более гибко:
источник
functools.update_wrapper
, чтобы сохранитьsayhi.__name__ == "sayhi"
Вы хотите следующую функцию при вызове:
Возвращаться:
Простое решение
Чтобы проще всего это сделать, создайте декораторы, которые возвращают лямбда-выражения (анонимные функции), которые закрывают функцию (замыкания) и вызывают ее:
Теперь используйте их по желанию:
и сейчас:
Проблемы с простым решением
Но мы, кажется, почти потеряли первоначальную функцию.
Чтобы найти его, нам нужно было бы покопаться в закрытии каждой лямбды, одна из которых похоронена в другой:
Поэтому, если мы помещаем документацию по этой функции, или хотим иметь возможность декорировать функции, которые принимают более одного аргумента, или мы просто хотим знать, какую функцию мы просматриваем в сеансе отладки, нам нужно сделать немного больше с нашими обертка.
Полнофункциональное решение - преодоление большинства из этих проблем
У нас есть декоратор
wraps
изfunctools
модуля в стандартной библиотеке!К сожалению, есть еще какой-то пример, но это настолько просто, насколько мы можем это сделать.
В Python 3 вы также получаете
__qualname__
и__annotations__
присваиваете по умолчанию.А сейчас:
И сейчас:
Вывод
Итак, мы видим, что
wraps
заставляет функцию обёртывания делать почти всё, кроме того, чтобы сказать нам точно, что функция принимает в качестве аргументов.Существуют и другие модули, которые могут пытаться решить проблему, но решения пока нет в стандартной библиотеке.
источник
Чтобы объяснить декоратор простым способом:
С:
Когда делать:
Вы действительно делаете:
источник
Декоратор берет определение функции и создает новую функцию, которая выполняет эту функцию и преобразует результат.
эквивалентно:
Пример:
Эта
эквивалентно этому
65 <=> 'а'
Чтобы понять декоратор, важно отметить, что декоратор создал новую функцию do, которая является внутренней, которая выполняет функцию и преобразует результат.
источник
print(do(65))
иprint(do2(65))
бытьA
иA
?Вы также можете написать декоратор в классе
источник
На этот ответ уже давно дан ответ, но я подумал, что поделюсь своим классом Decorator, который делает написание новых декораторов простым и компактным.
С одной стороны, я думаю, что это делает поведение декораторов очень ясным, но также позволяет очень просто определять новые декораторы. Для приведенного выше примера вы можете решить его следующим образом:
Вы также можете использовать его для выполнения более сложных задач, например, для декоратора, который автоматически делает рекурсивное применение функции ко всем аргументам в итераторе:
Какие отпечатки:
Обратите внимание, что в этом примере не был указан
list
тип в экземпляре декоратора, поэтому в последнем операторе print метод применяется к самому списку, а не к элементам списка.источник
Вот простой пример создания цепочек декораторов. Обратите внимание на последнюю строку - она показывает, что происходит под крышками.
Вывод выглядит так:
источник
Говоря о примере счетчика - как указано выше, счетчик будет разделен между всеми функциями, которые используют декоратор:
Таким образом, ваш декоратор может быть повторно использован для разных функций (или использован для декорирования одной и той же функции несколько раз:)
func_counter1 = counter(func); func_counter2 = counter(func)
, и переменная counter останется закрытой для каждой функции.источник
Украсьте функции с различным количеством аргументов:
Результат:
источник
def wrapper(*args, **kwargs):
иfn(*args, **kwargs)
.Ответ Паоло Бергантино имеет большое преимущество - он использует только stdlib и работает для этого простого примера, где нет аргументов декоратора или декорированной функции. .
Однако он имеет 3 основных ограничения, если вы хотите заняться более общими случаями:
makestyle(style='bold')
декоратора нетривиально.@functools.wraps
, не сохраняют подпись , поэтому, если предоставлены неверные аргументы, они начнут выполняться и могут вызвать ошибку другого типа, чем обычноTypeError
.@functools.wraps
чтобы получить доступ к аргументу , основанный на его имя . Действительно, аргумент может появляться в*args
, в**kwargs
или может не появляться вообще (если это необязательно).Я написал,
decopatch
чтобы решить первую проблему, и написал,makefun.wraps
чтобы решить две другие. Обратите внимание, чтоmakefun
использует тот же трюк, чем знаменитыйdecorator
lib.Вот как вы можете создать декоратор с аргументами, возвращая действительно сохраняющие подпись оболочки:
decopatch
предоставляет вам два других стиля разработки, которые скрывают или показывают различные концепции Python, в зависимости от ваших предпочтений. Самый компактный стиль выглядит следующим образом:В обоих случаях вы можете проверить работоспособность декоратора:
Пожалуйста, обратитесь к документации для деталей.
источник