¿Decoradores con parámetros?

Resuelto falek.marcin asked hace 13 años • 24 respuestas

Tengo un problema con la transferencia de la variable insurance_modepor parte del decorador. Lo haría mediante la siguiente declaración del decorador:

@execute_complete_reservation(True)
def test_booking_gta_object(self):
    self.test_select_gta_object()

pero lamentablemente esta afirmación no funciona. 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
falek.marcin avatar May 09 '11 00:05 falek.marcin
Aceptado

La sintaxis de 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. Entonces realmente debería devolver un decorador normal. Un poco confuso, ¿verdad? Lo que quiero decir es:

def decorator_factory(argument):
    def decorator(function):
        def wrapper(*args, **kwargs):
            funny_stuff()
            something_with_argument(argument)
            result = function(*args, **kwargs)
            more_funny_stuff()
            return result
        return wrapper
    return decorator

Aquí puede leer más sobre el tema; también es posible implementar esto usando objetos invocables y eso también se explica allí.

t.dubrownik avatar May 08 '2011 17:05 t.dubrownik

Editar : para comprender en profundidad el modelo mental de los decoradores, eche un vistazo a esta increíble charla sobre Pycon. Bien vale la pena los 30 minutos.

Una forma de pensar en los decoradores con argumentos es

@decorator
def foo(*args, **kwargs):
    pass

se traduce en

foo = decorator(foo)

Entonces, si el decorador tuviera argumentos,

@decorator_with_args(arg)
def foo(*args, **kwargs):
    pass

se traduce en

foo = decorator_with_args(arg)(foo)

decorator_with_argses 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 sencillo con parciales para facilitar la tarea de mis decoradores.

from functools import partial

def _pseudo_decor(fun, argument):
    def ret_fun(*args, **kwargs):
        #do stuff here, for eg.
        print ("decorator arg is %s" % str(argument))
        return fun(*args, **kwargs)
    return ret_fun

real_decorator = partial(_pseudo_decor, argument=arg)

@real_decorator
def foo(*args, **kwargs):
    pass

Actualizar:

Arriba, foose conviertereal_decorator(foo)

Un efecto de decorar una función es que el nombre foose anula tras la declaración del decorador. fooes "anulado" por lo que sea devuelto por real_decorator. En este caso, un nuevo objeto de función.

Todos los foometadatos de se anulan, en particular la cadena de documentos y el nombre de la función.

>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>

functools.wraps nos brinda un método conveniente para "elevar" la cadena de documentación y el nombre a la función devuelta.

from functools import partial, wraps

def _pseudo_decor(fun, argument):
    # magic sauce to lift the name and doc of the function
    @wraps(fun)
    def ret_fun(*args, **kwargs):
        # pre function execution stuff here, for eg.
        print("decorator argument is %s" % str(argument))
        returned_value =  fun(*args, **kwargs)
        # post execution stuff here, for eg.
        print("returned value is %s" % returned_value)
        return returned_value

    return ret_fun

real_decorator1 = partial(_pseudo_decor, argument="some_arg")
real_decorator2 = partial(_pseudo_decor, argument="some_other_arg")

@real_decorator1
def bar(*args, **kwargs):
    pass

>>> print(bar)
<function __main__.bar(*args, **kwargs)>

>>> bar(1,2,3, k="v", x="z")
decorator argument is some_arg
returned value is None
srj avatar Sep 13 '2014 19:09 srj