Tengo un problema con la transferencia de la variable 'insurance_mode' por el decorador. Lo haría con la siguiente declaración de decorador:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
pero desafortunadamente, esta afirmación no funciona. Quizás quizás haya una mejor manera de resolver este 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
toma dos parámetros, pero lo está pasando uno. Los decoradores son simplemente azúcar sintáctica para envolver funciones dentro de otras funciones. Consulte docs.python.org/reference/compound_stmts.html#function para obtener la documentación completa.Respuestas:
La sintaxis para los decoradores con argumentos es un poco diferente: el decorador con argumentos debe devolver una función que tomará una función y devolverá otra función. Por lo tanto, realmente debería devolver un decorador normal. Un poco confuso, ¿verdad? Lo que quiero decir es:
Aquí puede leer más sobre el tema: también es posible implementar esto utilizando objetos invocables y eso también se explica allí.
fuente
return function(*args, **kwargs)
@decorator()
y no solo@decorator
, incluso si solo tiene argumentos opcionales.Editar : para una comprensión profunda del modelo mental de decoradores, eche un vistazo a esto increíble Pycon Talk. Bien vale la pena los 30 minutos.
Una forma de pensar en decoradores con argumentos es
se traduce en
Entonces, si el decorador tenía argumentos,
se traduce en
decorator_with_args
es una función que acepta un argumento personalizado y que devuelve el decorador real (que se aplicará a la función decorada).Utilizo un truco simple con parciales para facilitar mis decoradores
Actualizar:
Arriba, se
foo
conviertereal_decorator(foo)
Un efecto de decorar una función es que el nombre
foo
se anula al declarar el decorador.foo
es "anulado" por lo que sea devuelto porreal_decorator
. En este caso, un nuevo objeto de función.Se
foo
anulan todos los metadatos, especialmente la cadena de documentos y el nombre de la función.functools.wraps nos brinda un método conveniente para "levantar" la cadena de documentos y el nombre de la función devuelta.
fuente
@functools.wraps
?functool.wraps
. Agregarlo en el ejemplo puede confundir aún más a los lectores.arg
aquí?bar
argumento dereal_decorator
?Me gustaría mostrar una idea que en mi humilde opinión es bastante elegante. La solución propuesta por t.dubrownik muestra un patrón que siempre es el mismo: necesita el envoltorio de tres capas independientemente de lo que haga el decorador.
Entonces pensé que este es un trabajo para un meta-decorador, es decir, un decorador para decoradores. Como un decorador es una función, en realidad funciona como un decorador regular con argumentos:
Esto se puede aplicar a un decorador regular para agregar parámetros. Entonces, por ejemplo, digamos que tenemos el decorador que duplica el resultado de una función:
Con
@parametrized
podemos construir un@multiply
decorador genérico que tenga un parámetroConvencionalmente, el primer parámetro de un parametrizado decorador es la función, mientras que los argumentos restantes corresponderán al parámetro del decorador parametrizado.
Un ejemplo de uso interesante podría ser un decorador asertivo de tipo seguro:
Una nota final: aquí no estoy usando
functools.wraps
para las funciones de contenedor, pero recomendaría usarlo todo el tiempo.fuente
@wraps
al mío para mi caso particular.@parametrized
truco. El problema que tuve fue que olvidé que la@
sintaxis es igual a las llamadas reales (de alguna manera lo sabía y no lo sabía al mismo tiempo que se puede deducir de mi pregunta). Entonces, si desea traducir la@
sintaxis en llamadas mundanas para verificar cómo funciona, es mejor que coméntelo temporalmente primero o termine llamándolo dos veces y obteniendo resultadosAquí hay una versión ligeramente modificada de la respuesta de t.dubrownik . ¿Por qué?
Entonces usa
@functools.wraps()
:fuente
Supongo que su problema es pasar argumentos a su decorador. Esto es un poco complicado y no es sencillo.
Aquí hay un ejemplo de cómo hacer esto:
Huellas dactilares:
Vea el artículo de Bruce Eckel para más detalles.
fuente
__name__
que no tendrá una instancia de la clase de decorador.class Foo: @MyDec(...) def method(self, ...): blah
que no funciona porqueFoo().method
no será un método vinculado y no se aprobaráself
automáticamente. Esto también se puede solucionar haciendoMyDec
un descriptor y creando métodos enlazados__get__
, pero es más complicado y mucho menos obvio. Al final, las clases de decorador no son tan convenientes como parecen.Uso del decorador
Entonces el
produce
pero
produce
fuente
Esta es una plantilla para un decorador de funciones que no requiere
()
si no se dan parámetros:Un ejemplo de esto se da a continuación:
fuente
factor_or_func
(o cualquier otro parámetro) nunca debe se reasigna enwrapper()
.locals()
?()
.En mi caso, decidí resolver esto mediante una lambda de una línea para crear una nueva función de decorador:
Cuando se ejecuta, esto imprime:
Quizás no sea tan extensible como otras soluciones, pero funcionó para mí.
fuente
¡Escribir un decorador que funcione con y sin parámetros es un desafío porque Python espera un comportamiento completamente diferente en estos dos casos! Muchas respuestas han tratado de solucionar esto y a continuación se muestra una mejora de la respuesta de @ norok2. Específicamente, esta variación elimina el uso de
locals()
.Siguiendo el mismo ejemplo dado por @ norok2:
Juega con este código .
El problema es que el usuario debe proporcionar pares de parámetros clave y de valor en lugar de parámetros posicionales y el primer parámetro está reservado.
fuente
Es bien sabido que las siguientes dos piezas de código son casi equivalentes:
Un error común es pensar que
@
simplemente oculta el argumento más a la izquierda.Sería mucho más fácil escribir decoradores si lo anterior es cómo
@
funcionó. Desafortunadamente, esa no es la forma en que se hacen las cosas.Considere un decorador
Wait
que frena la ejecución del programa durante unos segundos. Si no pasa un tiempo de espera, el valor predeterminado es 1 segundo. Los casos de uso se muestran a continuación.Cuando
Wait
tiene un argumento, como@Wait(3)
, entonces la llamadaWait(3)
se ejecuta antes que suceda algo más.Es decir, las siguientes dos piezas de código son equivalentes
Esto es un problema.
A continuación se muestra una solución:
Comencemos creando la siguiente clase
DelayedDecorator
:Ahora podemos escribir cosas como:
Tenga en cuenta que:
dec
no acepta múltiples argumentos.dec
solo acepta la función a envolver.importar inspeccionar clase PolyArgDecoratorMeta (type): def call (Wait, * args, ** kwargs): try: arg_count = len (args) if (arg_count == 1): if callable (args [0]): SuperClass = inspect. getmro (PolyArgDecoratorMeta) [1] r = Superclase. llamada (Wait, args [0]) else: r = DelayedDecorator (Wait, * args, ** kwargs) else: r = DelayedDecorator (Wait, * args, ** kwargs) finalmente: pasar retorno r
import time class Wait (metaclass = PolyArgDecoratorMeta): def init (i, func, delay = 2): i._func = func i._delay = delay
Las siguientes dos piezas de código son equivalentes:
Podemos imprimir
"something"
en la consola muy lentamente, de la siguiente manera:Notas finales
Puede parecer mucho código, pero no tiene que escribir las clases
DelayedDecorator
yPolyArgDecoratorMeta
todo el tiempo. El único código que tiene que escribir personalmente es algo como lo siguiente, que es bastante corto:fuente
defina esta "función decoradora" para generar una función decoradora personalizada:
úsalo de esta manera:
fuente
Grandes respuestas arriba. Este también ilustra
@wraps
, que toma la cadena de documentación y el nombre de la función de la función original y la aplica a la nueva versión envuelta:Huellas dactilares:
fuente
En caso de que tanto la función como el decorador tengan que tomar argumentos, puede seguir el siguiente enfoque.
Por ejemplo, hay un decorador llamado
decorator1
que toma un argumentoAhora, si el
decorator1
argumento tiene que ser dinámico, o pasado mientras se llama a la función,En el código anterior
seconds
es el argumento paradecorator1
a, b
son los argumentos defunc1
fuente