У меня проблема с передачей переменной 'insurance_mode' декоратором. Я сделал бы это следующим оператором декоратора:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
но, к сожалению, это утверждение не работает. Возможно, может быть есть лучший способ решить эту проблему.
def execute_complete_reservation(test_case,insurance_mode):
def inner_function(self,*args,**kwargs):
self.test_create_qsf_query()
test_case(self,*args,**kwargs)
self.test_select_room_option()
if insurance_mode:
self.test_accept_insurance_crosseling()
else:
self.test_decline_insurance_crosseling()
self.test_configure_pax_details()
self.test_configure_payer_details
return inner_function
execute_complete_reservation
принимает два параметра, но вы передаете его один. Декораторы - это просто синтаксический сахар для обёртывания функций внутри других функций. См. Docs.python.org/reference/compound_stmts.html#function для полной документации.Ответы:
Синтаксис декораторов с аргументами немного отличается - декоратор с аргументами должен возвращать функцию, которая будет принимать функцию и возвращать другую функцию. Так что действительно должен вернуть нормальный декоратор. Немного смущает, верно? Я имею в виду:
Здесь вы можете прочитать больше на эту тему - это также можно реализовать с помощью вызываемых объектов, что также объясняется там.
источник
return function(*args, **kwargs)
@decorator()
а не только@decorator
, даже если у вас есть только необязательные аргументы.Изменить : для глубокого понимания ментальной модели декораторов, взгляните на этот удивительный Pycon Talk. стоит 30 минут.
Один из способов думать об декораторах с аргументами
переводит на
Так что, если у декоратора были аргументы,
переводит на
decorator_with_args
является функцией, которая принимает пользовательский аргумент и возвращает фактический декоратор (который будет применен к декорированной функции).Я использую простой трюк с частями, чтобы сделать мои декораторы легкими
Обновить:
Выше
foo
становитсяreal_decorator(foo)
Одним из эффектов декорирования функции является то, что имя
foo
переопределяется при объявлении декоратора.foo
«отменяется» тем, что возвращаетсяreal_decorator
. В этом случае новая функция объекта.Все
foo
метаданные переопределяются, в частности, строка документации и имя функции.functools.wraps предоставляет нам удобный метод для «поднятия» строки документации и имени возвращаемой функции.
источник
@functools.wraps
?functool.wraps
. Добавление этого в пример может еще больше запутать читателей.arg
здесь?bar
аргументуreal_decorator
?Я хотел бы показать идею, которая является ИМХО довольно элегантной. Решение, предложенное t.dubrownik, показывает шаблон, который всегда один и тот же: вам нужна трехслойная оболочка независимо от того, что делает декоратор.
Поэтому я подумал, что это работа для мета-декоратора, то есть декоратора для декораторов. Поскольку декоратор является функцией, он фактически работает как обычный декоратор с аргументами:
Это может быть применено к обычному декоратору для добавления параметров. Так, например, скажем, у нас есть декоратор, который удваивает результат функции:
С помощью
@parametrized
мы можем построить общий@multiply
декоратор, имеющий параметрТрадиционно первым параметром параметризованного декоратора является функция, в то время как остальные аргументы будут соответствовать параметру параметризованного декоратора.
Интересным примером использования может быть тип-безопасный ассертивный декоратор:
И последнее замечание: здесь я не использую
functools.wraps
функции-оболочки, но я бы рекомендовал использовать его все время.источник
@wraps
по моему конкретному делу.@parametrized
трюк. Проблема, с которой я столкнулся, заключалась в том, что я забыл, что@
синтаксис равен фактическим вызовам (почему-то я знал это и не знал этого одновременно, как вы можете понять из моего вопроса). Поэтому, если вы хотите перевести@
синтаксис в обычные вызовы, чтобы проверить, как он работает, вам лучше сначала временно закомментировать его, иначе вам придется дважды вызывать его и получать результатыВот слегка измененная версия ответа t.dubrownik . Почему?
Так что используйте
@functools.wraps()
:источник
Я предполагаю, что ваша проблема заключается в передаче аргументов вашему декоратору. Это немного сложно и не просто.
Вот пример того, как это сделать:
Печать:
Смотрите статью Брюса Экеля для более подробной информации.
источник
__name__
которые не будут иметь экземпляр класса декоратора?class Foo: @MyDec(...) def method(self, ...): blah
который не работает, потомуFoo().method
что не будет связанным методом и не пройдетself
автоматически. Это тоже можно исправить, создавMyDec
дескриптор и создав связанные методы__get__
, но это более сложно и менее очевидно. В конце концов, классы декораторов не так удобны, как кажутся.Использование декоратора
Тогда
производит
но
производит
источник
Это шаблон для декоратора функции, который не требует,
()
если не нужно указывать никаких параметров:пример этого приведен ниже:
источник
factor_or_func
(или любой другой параметр) никогда не должен получает переназначен вwrapper()
.locals()
?()
.В моем случае я решил решить эту проблему с помощью однострочной лямбды, чтобы создать новую функцию декоратора:
Когда выполнено, это печатает:
Возможно, не так расширяемо, как другие решения, но сработало для меня.
источник
Написание декоратора, который работает с параметрами и без них, является сложной задачей, потому что Python ожидает совершенно различное поведение в этих двух случаях! Многие ответы пытались обойти это, и ниже приведено улучшение ответа от @ norok2. В частности, этот вариант исключает использование
locals()
.Следуя тому же примеру, что и @ norok2:
Играй с этим кодом .
Уловка в том, что пользователь должен предоставить ключ, пары значений параметров вместо позиционных параметров, и первый параметр зарезервирован.
источник
Хорошо известно, что следующие два фрагмента кода почти эквивалентны:
Распространенная ошибка - думать, что это
@
просто скрывает самый левый аргумент.Было бы намного проще написать декораторы, если бы вышеприведенное
@
работало так. К сожалению, дело обстоит не так.Рассмотрим декоратор,
Wait
который мешает выполнению программы в течение нескольких секунд. Если вы не передаете время ожидания, то значение по умолчанию составляет 1 секунду. Варианты использования приведены ниже.Когда
Wait
есть аргумент, такой как@Wait(3)
, тогда вызовWait(3)
выполняется прежде, чем что-либо еще произойдет.То есть следующие две части кода эквивалентны
Это проблема.
Одно решение показано ниже:
Давайте начнем с создания следующего класса
DelayedDecorator
:Теперь мы можем написать такие вещи, как:
Обратите внимание, что:
dec
не принимает множественные аргументы.dec
принимает только функцию для переноса.класс проверки импорта PolyArgDecoratorMeta (тип): вызов def (Wait, * args, ** kwargs): try: arg_count = len (args) if (arg_count == 1): if callable (args [0]): SuperClass = inspect. getmro (PolyArgDecoratorMeta) [1] r = Суперкласс. call (Wait, args [0]) else: r = DelayedDecorator (Wait, * args, ** kwargs) else: r = DelayedDecorator (Wait, * args, ** kwargs) наконец: pass return r
класс времени импорта Wait (metaclass = PolyArgDecoratorMeta): def init (i, func, delay = 2): i._func = func i._delay = delay
Следующие две части кода эквивалентны:
Мы можем печатать
"something"
на консоли очень медленно, следующим образом:Заключительные заметки
Это может выглядеть как много кода, но вам не нужно писать классы
DelayedDecorator
иPolyArgDecoratorMeta
каждый раз. Единственный код, который вы должны лично написать что-то вроде следующего, что довольно коротко:источник
Определите эту «функцию decoratorize» для генерации настраиваемой функции decorator:
используйте это так:
источник
Отличные ответы выше. Этот пример также иллюстрирует
@wraps
, который берет строку документа и имя функции из исходной функции и применяет ее к новой упакованной версии:Печать:
источник
Если и функция, и декоратор должны принимать аргументы, вы можете следовать нижеприведенному подходу.
Например, есть декоратор с именем,
decorator1
который принимает аргументТеперь, если
decorator1
аргумент должен быть динамическим или передаваться при вызове функции,В приведенном выше коде
seconds
это аргумент в пользуdecorator1
a, b
аргументыfunc1
источник