Estou com um problema com a transferência da variável 'insurance_mode' pelo decorador. Eu o faria pela seguinte declaração do decorador:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
mas, infelizmente, esta afirmação não funciona. Talvez haja uma maneira melhor de resolver esse problema.
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
requer dois parâmetros, mas você está passando um. Decoradores são apenas açúcar sintático para envolver funções dentro de outras funções. Consulte docs.python.org/reference/compound_stmts.html#function para obter a documentação completa.Respostas:
A sintaxe para decoradores com argumentos é um pouco diferente - o decorador com argumentos deve retornar uma função que terá uma função e retornará outra função. Portanto, ele realmente deve retornar um decorador normal. Um pouco confuso, certo? O que eu quero dizer é:
Aqui você pode ler mais sobre o assunto - também é possível implementar isso usando objetos de chamada e isso também é explicado lá.
fonte
return function(*args, **kwargs)
@decorator()
e não apenas@decorator
, mesmo se tiver apenas argumentos opcionais.Edit : para uma compreensão profunda do modelo mental dos decoradores, dê uma olhada neste incrível Pycon Talk. vale a pena os 30 minutos.
Uma maneira de pensar sobre decoradores com argumentos é
traduz para
Então, se o decorador tiver argumentos,
traduz para
decorator_with_args
é uma função que aceita um argumento personalizado e retorna o decorador real (que será aplicado à função decorada).Eu uso um truque simples com parciais para facilitar meus decoradores
Atualizar:
Acima,
foo
torna-sereal_decorator(foo)
Um efeito de decorar uma função é que o nome
foo
é substituído na declaração do decorador.foo
é "substituído" por tudo o que é retornado porreal_decorator
. Nesse caso, um novo objeto de função.Tudo de
foo
os metadados de são substituídos, principalmente docstring e nome da função.functools.wraps nos fornece um método conveniente para "levantar" a docstring e o nome da função retornada.
fonte
@functools.wraps
?functool.wraps
. Adicioná-lo no exemplo pode confundir ainda mais os leitores.arg
aqui?bar
para o argumento dereal_decorator
?Eu gostaria de mostrar uma ideia que é IMHO bastante elegante. A solução proposta por t.dubrownik mostra um padrão que é sempre o mesmo: você precisa do wrapper de três camadas, independentemente do que o decorador faz.
Então eu pensei que este é um trabalho para um meta-decorador, ou seja, um decorador para decoradores. Como um decorador é uma função, ele realmente funciona como um decorador regular com argumentos:
Isso pode ser aplicado a um decorador regular para adicionar parâmetros. Por exemplo, digamos que temos o decorador que dobra o resultado de uma função:
Com
@parametrized
nós podemos construir um@multiply
decorador genérico com um parâmetroConvencionalmente, o primeiro parâmetro de um parâmetro decorador é a função, enquanto os argumentos restantes corresponderão ao parâmetro do decorador parametrizado.
Um exemplo de uso interessante pode ser um decorador assertivo com segurança de tipo:
Uma observação final: aqui não estou usando
functools.wraps
para as funções do wrapper, mas eu recomendaria usá-lo o tempo todo.fonte
@wraps
minha para o meu caso particular.@parametrized
truque. O problema que tive foi que esqueci que a@
sintaxe é igual a chamadas reais (de alguma forma eu sabia disso e não sabia disso ao mesmo tempo que você pode entender da minha pergunta). Portanto, se você deseja traduzir a@
sintaxe em chamadas comuns, para verificar como ele funciona, é melhor comentar temporariamente primeiro ou acabar chamando duas vezes e obtendo resultados mumbojumboAqui está uma versão ligeiramente modificada da resposta de t.dubrownik . Por quê?
Então use
@functools.wraps()
:fonte
Presumo que seu problema seja passar argumentos para o seu decorador. Isso é um pouco complicado e não é direto.
Aqui está um exemplo de como fazer isso:
Impressões:
Veja o artigo de Bruce Eckel para mais detalhes.
fonte
__name__
uma instância da classe decoradora não terá?class Foo: @MyDec(...) def method(self, ...): blah
qual me referi foi o que não funciona porqueFoo().method
não será um método vinculado e não será aprovadoself
automaticamente. Isso também pode ser corrigido, criandoMyDec
um descritor e criando métodos vinculados__get__
, mas é mais envolvido e muito menos óbvio. No final, as aulas de decorador não são tão convenientes quanto parecem.Uso do decorador
Então o
produz
mas
produz
fonte
Este é um modelo para um decorador de funções que não requer
()
que nenhum parâmetro seja fornecido:um exemplo disso é dado abaixo:
fonte
factor_or_func
(ou qualquer outro parâmetro) nunca deve se transferido emwrapper()
.locals()
?()
.No meu exemplo, decidi resolver isso por meio de uma lambda de uma linha para criar uma nova função de decorador:
Quando executado, isso imprime:
Talvez não seja tão extensível quanto outras soluções, mas funcionou para mim.
fonte
Escrever um decorador que funcione com e sem parâmetro é um desafio, porque o Python espera um comportamento completamente diferente nesses dois casos! Muitas respostas tentaram contornar isso e abaixo está uma melhoria da resposta por @ norok2. Especificamente, essa variação elimina o uso de
locals()
.Seguindo o mesmo exemplo dado por @ norok2:
Brinque com este código .
O problema é que o usuário deve fornecer chave, pares de parâmetros de valor em vez de parâmetros posicionais e o primeiro parâmetro é reservado.
fonte
É sabido que os dois seguintes códigos são quase equivalentes:
Um erro comum é pensar que
@
simplesmente oculta o argumento mais à esquerda.Seria muito mais fácil escrever decoradores se o que precede é como
@
funcionou. Infelizmente, não é assim que as coisas são feitas.Considere um decorador
Wait
que interrompe a execução do programa por alguns segundos. Se você não passar um tempo de espera, o valor padrão será 1 segundo. Os casos de uso são mostrados abaixo.Quando
Wait
tem um argumento, como@Wait(3)
, então, a chamadaWait(3)
é executada antes que qualquer outra coisa aconteça.Ou seja, os dois seguintes códigos são equivalentes
Isto é um problema.
Uma solução é mostrada abaixo:
Vamos começar criando a seguinte classe
DelayedDecorator
:Agora podemos escrever coisas como:
Observe que:
dec
não aceita vários argumentos.dec
aceita apenas a função a ser quebrada.importar inspecione a classe PolyArgDecoratorMeta (tipo): def call (Aguarde, * args, ** kwargs): tente: arg_count = len (args) if (arg_count == 1): se for possível chamar (args [0]): SuperClass = inspecionar. getmro (PolyArgDecoratorMeta) [1] r = SuperClasse. ligar (Aguarde, args [0]) else: r = DelayedDecorator (espera, * args, ** kwargs) else: r = DelayedDecorator (espera, * args, ** kwargs) finalmente: passa retorno r
classe de tempo de importação Wait (metaclass = PolyArgDecoratorMeta): def init (i, func, delay = 2): i._func = func i._delay = delay
Os dois seguintes pedaços de código são equivalentes:
Podemos imprimir
"something"
no console muito lentamente, da seguinte maneira:Notas Finais
Pode parecer muito código, mas você não precisa escrever as classes
DelayedDecorator
ePolyArgDecoratorMeta
sempre. O único código que você precisa escrever pessoalmente é o seguinte, que é bastante curto:fonte
defina esta "função decoratorize" para gerar a função decorator personalizada:
use-o desta maneira:
fonte
Ótimas respostas acima. Este também ilustra
@wraps
, que pega a sequência de documentos e o nome da função da função original e a aplica à nova versão agrupada:Impressões:
fonte
Caso a função e o decorador precisem usar argumentos, você pode seguir a abordagem abaixo.
Por exemplo, há um decorador chamado
decorator1
que recebe um argumentoAgora, se o
decorator1
argumento precisar ser dinâmico ou passado ao chamar a função,No código acima
seconds
é o argumento paradecorator1
a, b
são os argumentos defunc1
fonte