2009-06-29 9 views
5

Sono eccitato di vedere l'ultima versione del modulo python decorator (3.0). Sembra molto più pulito (ad esempio la sintassi è più zuccherosa che mai) rispetto alle precedenti iterazioni.Python Decorator 3.0 e argomenti per il decoratore

Tuttavia, sembra avere un pessimo supporto (ad esempio sintassi "aspro", per estendere orribilmente la metafora) per decoratori che prendono argomenti da soli. Qualcuno ha un buon esempio per come lo si dovrebbe fare usando semplicemente 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

home decoratore: http://pypi.python.org/pypi/decorator/ –

risposta

7

In questo caso, è necessario rendere la funzione restituire il decoratore. (Tutto può essere risolto con un altro livello di riferimento indiretto ...)

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 

Ciò significa substitute_args non è un decoratore stesso, è un decoratore fabbrica. Ecco l'equivalente senza il modulo 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 

tre livelli di profondità non è molto conveniente, ma ricordate due di loro sono in fase di definizione della funzione:

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

che è equivalente a:

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

questo è stato estremamente utile. Grazie! – YGA

-2

qui è un altro modo che ho appena scoperto: controlla se il primo (e unico) argomento del tuo decoratore è callable; se è così, hai finito e puoi restituire il tuo metodo di modifica del comportamento (a sua volta decorato con functools.wraps per preservare il nome e la stringa di documentazione).

nell'altro caso, devono essere presenti uno o più argomenti denominati o posizionali; puoi raccogliere quegli argomenti e restituire un callable che accetta un callable come primo argomento e restituisce un metodo wrapper, e poiché quella descrizione si adatta alla descrizione del metodo decoratore, restituisci il metodo stesso decoratore! ho usato functools.partial qui per ottenere una versione del mio decoratore, is_global_method (che sto lavorando proprio ora - la sua implementazione è ovviamente senza senso come mostrato di seguito, questo è solo per dimostrare le opere di decorazione).

questa soluzione sembra funzionare ma sicuramente richiede più test. se ti trapani gli occhi, puoi vedere che il trucco è solo tre o quattro linee come schema da ricordare. ora mi chiedo se posso avvolgere questo tipo di funzionalità in un altro decoratore? ah, la mancanza di esso!

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)) 
Problemi correlati