2016-04-05 36 views
5

Sto imparando un po 'sui decoratori da un ottimo tutorial su thecodeship ma mi sono trovato piuttosto confuso da un esempio.Esempio di decoratore in pitone

Prima un semplice esempio seguito da una spiegazione per ciò che un decoratore è.

def p_decorate(func): 
    def func_wrapper(name): 
     return "<p>{0}</p>".format(func(name)) 
    return func_wrapper 

def get_text(name): 
    return "lorem ipsum, {0} dolor sit amet".format(name) 

my_get_text = p_decorate(get_text) 

print my_get_text("John") 

Ora questo ha senso per me. Un decoratore è semplicemente un involucro per una funzione. E in questa spiegazione di ragazzi dice che un decoratore è una funzione che prende un'altra funzione come argomento, genera una nuova funzione e restituisce la funzione generata da usare ovunque.

E ora l'equivalente del codice di cui sopra è:

def p_decorate(func): 
    def func_wrapper(name): 
     return "<p>{0}</p>".format(func(name)) 
    return func_wrapper 

@p_decorate 
def get_text(name): 
    return "lorem ipsum, {0} dolor sit amet".format(name) 

print get_text("John") 

Credo di capire il modo in cui un decoratore viene inizializzato quando somministrato senza argomenti. Correggimi se sbaglio

  • Il decoratore di default passa nella funzione get_text e perché p_decorate restituisce una funzione func_wrapper, si finisce con l'affermazione vera get_text = func_wrapper.

Ciò che è importante per me è il primo equivalente di codice, perché vedo e capisco come si comporta il decoratore.

La cosa che mi confonde molto è il seguente codice:

def tags(tag_name): 
    def tags_decorator(func): 
     def func_wrapper(name): 
      return "<{0}>{1}</{0}>".format(tag_name, func(name)) 
     return func_wrapper 
    return tags_decorator 

@tags("p") 
def get_text(name): 
    return "Hello "+name 

print get_text("John") 

Anche in questo caso, mi corregga se sbaglio, ma questa è la mia comprensione.

  • Il decoratore accetta la stringa di tag "p" al posto del nome della funzione di default . E a sua volta la funzione tags_decorator presuppone che il parametro che verrà passato sia la funzione decorata, get_text.

Potrebbe essere utile per me vedere il blocco di codice equivalente in forma "non decoratore", ma non riesco a capire come potrebbe sembrare. Inoltre, non capisco perché sia ​​restituito tags_decorator e func_wrapper. Qual è lo scopo di restituire due funzioni diverse se un decoratore deve solo restituire 1 funzione per avvolgere get_text.

Come nota a margine, in realtà si riduce a quanto segue.

  • Questo blocco può essere semplificato a qualcosa di meno di un insieme di 3 funzioni?
  • I decoratori accettano più di 1 argomento per semplificare il codice?

risposta

7

Entro certi limiti, tutto dopo il @ viene eseguita per prodotti un decoratore.Nel tuo primo esempio, ciò che segue dopo la @ è solo un nome:

@p_decorate 

così Python guarda in alto p_decorate e chiede con la funzione decorata come argomento:

get_text = p_decorate(get_text) 

(semplificato un po ', get_text non è inizialmente associato alla funzione originale, ma hai già l'essenza).

Nel tuo secondo esempio, l'espressione decoratore è un po 'più coinvolto, include una chiamata:

@tags("p") 

così Python usa tags("p") per trovare il decoratore. Alla fine questo è quello che poi viene eseguito:

get_text = tags("p")(get_text) 

Il uscita del tags("p") è decoratore qui! Io chiamo la funzione tags come decoratore factory, produce un decoratore quando viene chiamato. Quando si chiama tags(), restituisce tags_decorator(). Questo è il decoratore reale qui.

Si potrebbe rimuovere invece la funzione di decoratore e hardcode il valore "p" e l'uso che direttamente:

def tags_decorator_p(func): 
    def func_wrapper(name): 
     return "<{0}>{1}</{0}>".format("p", func(name)) 
    return func_wrapper 

@tags_decorator_p 
def get_text(name): 
    # ... 

ma allora si sarebbe necessario creare decoratori separati per ogni possibile valore dell'argomento a tags(). Questo è il valore di una fabbrica di arredatori, puoi aggiungere parametri al decoratore e modificare il modo in cui la tua funzione è decorata.

Un decoratore factory può richiedere un numero qualsiasi di argomenti; è solo una funzione che chiami per produrre un decoratore. Il decoratore stesso può accettare solo un argomento, la funzione per decorare.

Ho detto, entro i limiti all'inizio della mia risposta per un motivo; la sintassi per l'espressione che segue lo @ consente solo un nome puntato (foo.bar.baz, quindi l'accesso agli attributi) e una chiamata ((arg1, arg2, keyword=arg3)). Vedi lo reference documentation. Gli stati PEP 318 originali:

La dichiarazione del decoratore è limitata in ciò che può accettare: le espressioni arbitrarie non funzionano. Guido preferiva questo a causa di una sensazione istintiva [17].

+0

Grazie. E ho fatto una breve modifica dopo la tua risposta. Perché esattamente queste serie di funzioni restituiscono 2 funzioni e non 1? – Max

+1

@Max: vedere la mia ultima modifica. Uno è il decoratore (prodotto dalla fabbrica del decoratore), l'altro è l'involucro che sostituisce la funzione originale, il risultato del decoratore. –

+0

Capisco ora, lo scopo delle 2 chiamate di funzione. Sono in grado di creare un decoratore specifico con la fabbrica di decorazioni per specificare ulteriormente il mio involucro finale. – Max