2009-11-23 9 views
40

Ho un problema nell'utilizzo delle docstring con i decoratori. Dato il seguente esempio:Decoratore in pitone per il controllo delle docce

def decorator(f): 
    def _decorator(): 
     print 'decorator active' 
     f() 
    return _decorator 

@decorator 
def foo(): 
    '''the magic foo function''' 
    print 'this is function foo' 

help(foo) 

Ora l'aiuto non mi mostrare la docstring di foo come previsto, si vede:

Help on function _decorator in module __main__: 

_decorator() 

Senza il decoratore, l'aiuto è corretto:

Help on function foo in module __main__: 

foo() 
    the magic foo function 

Lo so, che la funzione foo è avvolta dal decoratore e quindi l'oggetto funzione non è più la funzione foo. Ma qual è una buona soluzione per ottenere docstring (e l'aiuto) come previsto?

risposta

62

Usa functools.wraps() per aggiornare gli attributi del decoratore:

from functools import wraps 

def decorator(f): 
    @wraps(f) 
    def _decorator(): 
     print 'decorator active' 
     f() 
    return _decorator 

@decorator 
def foo(): 
    '''the magic foo function''' 
    print 'this is function foo' 

help(foo) 

vedere anche la Standard Library documentation per functools.

+0

Questo non funziona se 'foo' prende degli argomenti - vengono sostituiti da qualsiasi cosa usi' _decorator'. Questo è un problema specialmente quando vuoi che il tuo decoratore prenda '* args, ** kwds'. Non sono mai stato in grado di trovare un modo per ottenere correttamente la docstring usando 'functools.wraps'. –

+3

@Scott Griffiths: la docstring sarà ancora corretta anche se 'foo' accetta argomenti. Tuttavia, 'help (foo)' visualizzerà la lista dei parametri di '_decorator', dato che in realtà sostituisce la funzione' foo'. Non c'è alcun modo per aggirare questo se stai scrivendo decoratori che accettano argomenti arbitrari usando '* args, ** kwargs', ma per me il punto importante è che la docstring sia mantenuta intatta. I dettagli dei parametri possono sempre essere specificati nella docstring per maggiore chiarezza. –

+0

Grazie per le informazioni extra. Recentemente non sono riuscito a ottenere la descrizione della guida corretta per le funzioni decorate: mi sembra un pessimo stato di cose, ma capisco la difficoltà in quanto la funzione decorata potrebbe avere una firma completamente diversa. Tuttavia, ci deve essere un modo ... :) –

12

ho trovato una soluzione, ma non so se è davvero bello:

def decorator(f): 
    def _decorator(): 
     print 'decorator active' 
     f() 
    _decorator.__name__=f.__name__ 
    _decorator.__doc__=f.__doc__ 
    return _decorator 

La parte con _decorator.__name__=f.__name__ sembra un po 'orribile ... Cosa ne pensi?

+4

In realtà questo è (quasi?) Esattamente ciò che fanno functools.wraps :) – thomas

+2

Non sembra orribile da me. Dice esattamente quello che vuoi dire. "Voglio che il nome di questa funzione sia 'myfunction' anziché '_decorator'." – jcdyer

-5

E 'probabilmente un po' vecchio ora, ma questo è come lo fai. Basta fare in modo che @decorator è rientrato sulla stessa linea come def decorator(f):

from functools import wraps 

def decorator(f): 
    @wraps(f) 
    def _decorator(): 
     print 'decorator active' 
     return f() 
    return _decorator 

@decorator 
def foo(): 
    '''the magic foo function''' 
    print 'this is function foo' 

help(foo) 
+8

Questa è solo la risposta già accettata. –