2009-06-29 11 views
5

Estoy emocionado de ver la última versión del módulo decorator python (3.0). Se ve mucho más limpio (por ejemplo, la sintaxis es más azucarada que nunca) que las iteraciones anteriores.Python Decorator 3.0 y argumentos para el decorador

Sin embargo, parece tener un pésimo soporte (por ejemplo, sintaxis "agria", para estirar horriblemente la metáfora) para los decoradores que toman los argumentos por sí mismos. ¿Alguien tiene un buen ejemplo de cómo lo haría limpiamente usando decorator 3.0?

def substitute_args(fun, arg_sub_dict): 
     def wrapper(arg): 
     new_arg = arg_sub_dict.get(arg, arg) 
     return fun(new_arg) 

     # some magic happens here to make sure that type signature, 
     # __name__, __doc__, etc. of wrapper matches fun 

return wrapper 
+0

decorador de casa: http://pypi.python.org/pypi/decorator/ –

Respuesta

7

En este caso, debe hacer que su función devuelva el decorador. (Cualquier cosa puede ser resuelto por otro nivel de indirección ...)

from decorator import decorator 
def substitute_args(arg_sub_dict): 
    @decorator 
    def wrapper(fun, arg): 
    new_arg = arg_sub_dict.get(arg, arg) 
    return fun(new_arg) 
    return wrapper 

Esto significa substitute_args no es un decorador en sí, es un decorador fábrica. Aquí está el equivalente sin el módulo decorator.

def substitute_args(arg_sub_dict): 
    def my_decorator(fun): 
    def wrapper(arg): 
     new_arg = arg_sub_dict.get(arg, arg) 
     return fun(new_arg) 
    # magic to update __name__, etc. 
    return wrapper 
    return my_decorator 

tres niveles de profundidad no es muy conveniente, pero recuerda que dos de ellos son cuando se define la función:

@substitute_args({}) # this function is called and return value is the decorator 
def f(x): 
    return x 
# that (anonymous) decorator is applied to f 

lo que equivale a:

def f(x): 
    return x 
f = substitude_args({})(f) # notice the double call 
+0

esto fue muy útil. ¡Gracias! – YGA

-2

aquí es otra forma que acabo de descubrir: compruebe si el primer (y único) argumento para su decorador es invocable; si es así, has terminado y puedes devolver tu método de envoltura modificadora de comportamiento (decorado con functools.wraps para preservar el nombre y la cadena de documentación).

en el otro caso, uno o más argumentos nombrados o posicionales deberían estar presentes; puede recopilar esos argumentos y devolver un invocable que acepte un primer argumento invocable y devuelva un método de envoltura, y dado que esa descripción se ajusta a la descripción del método de decorador, ¡devuelva ese mismo método de decorador! He usado functools.partial aquí para obtener una versión de mi decorador, is_global_method (que estoy trabajando en este momento; su implementación es, por supuesto, absurda, como se muestra a continuación, esto es solo para demostrar que la decoración funciona).

esta solución parece funcionar, pero seguro necesita más pruebas. si quieses nuestros ojos, puedes ver que el truco es solo tres o cuatro líneas como un patrón para recordar. ahora me pregunto si puedo incluir ese tipo de funcionalidad en otro decorador. ah, la metaness de eso!

from functools import wraps 
from functools import partial 

_    = print 
is_instance_of = isinstance 
is_callable  = lambda x: hasattr(x, '__call__') 

def is_global_method(x, *, name = None): 
    if is_callable(x): 
    @wraps(x) 
    def wrapper(*P, **Q): 
     return { 'name': name, 'result': x(*P, **Q), } 
    return wrapper 
    # assert is_instance_of(x, str) # could do some sanity checks here 
    return partial(is_global_method, name = x) 

@is_global_method 
def f(x): 
    """This is method f.""" 
    return x ** 2 

@is_global_method('foobar') 
def g(x): 
    """This is method g.""" 
    return x ** 2 

_(f.__name__) 
_(f.__doc__) 
_(f(42)) 
_(g.__name__) 
_(g.__doc__) 
_(g(42)) 
Cuestiones relacionadas