Ordine di esecuzione del decoratore


93
def make_bold(fn):
    return lambda : "<b>" + fn() + "</b>"

def make_italic(fn):
    return lambda : "<i>" + fn() + "</i>"

@make_bold
@make_italic
def hello():
  return "hello world"

helloHTML = hello()

Produzione: "<b><i>hello world</i></b>"

Capisco approssimativamente i decoratori e come funziona con uno di essi nella maggior parte degli esempi.

In questo esempio, ce ne sono 2. Dall'output, sembra che venga @make_italiceseguito prima, quindi @make_bold.

Questo significa che per le funzioni decorate, eseguirà prima la funzione per poi spostarsi verso l'alto per gli altri decoratori? Come @make_italicprima allora @make_bold, invece del contrario.

Quindi questo significa che è diverso dalla norma dell'approccio top-down nella maggior parte dei linguaggi di programmazione? Solo per questo caso di decoratore? O mi sbaglio?


4
sì, si parte dal basso verso l'alto passando il risultato al successivo
Padraic Cunningham

1
Anche il commento di @PadraicCunningham è una parte importante della risposta. Abbiamo avuto un problema correlato ( stackoverflow.com/questions/47042196/... )
shookees

Direi che è ancora top-down, nel senso che a(b(x))è top-down (se immagini quella suddivisione su 3 righe)
joel

Risposte:


126

I decoratori avvolgono la funzione che stanno decorando. Così make_bolddecorato il risultato del make_italicdecoratore, che ha decorato la hellofunzione.

La @decoratorsintassi è in realtà solo zucchero sintattico; il seguente:

@decorator
def decorated_function():
    # ...

è realmente eseguito come:

def decorated_function():
    # ...
decorated_function = decorator(decorated_function)

sostituire l' decorated_functionoggetto originale con quanto decorator()restituito.

I decoratori impilati ripetono quel processo verso l'esterno .

Quindi il tuo campione:

@make_bold
@make_italic
def hello():
  return "hello world"

può essere espanso a:

def hello():
  return "hello world"
hello = make_bold(make_italic(hello))

Quando chiami hello()adesso, stai chiamando l'oggetto restituito da make_bold(), davvero. make_bold()ha restituito un valore lambdache chiama la funzione make_boldwrapping, che è il valore restituito di make_italic(), che è anche un lambda che chiama l'originale hello(). Espandendo tutte queste chiamate ottieni:

hello() = lambda : "<b>" + fn() + "</b>" #  where fn() ->
    lambda : "<i>" + fn() + "</i>" # where fn() -> 
        return "hello world"

quindi l'output diventa:

"<b>" + ("<i>" + ("hello world") + "</i>") + "</b>"

Capisco. Ma questo significa che quando ci sono 2 wrapper in questo caso, l'IDE rileverà automaticamente e avvolgerà il risultato del primo wrapper? Perché lo pensavo @make_bold #make_bold = make_bold(hello) @make_italic #make_italic = make_italic (hello)? Non sono sicuro se in base a questo, avvolgerà il primo risultato. O per questo caso di 2 wrapper, l'IDE utilizzerà make_bold(make_italic(hello))come hai detto al posto di quello che ho condiviso?
Newbie

3
@Newbie: il tuo IDE non fa nulla qui; è Python che fa il wrapping. Ti ho mostrato nel mio ultimo esempio che make_bold()avvolge l'output di make_italic(), che è stato utilizzato per avvolgere hello, quindi l'equivalente di make_bold(make_italic(hello)).
Martijn Pieters

Potresti fornire una versione di questo codice senza l'uso di lambda? Avevo provato .format ma non funziona. E perché lambda viene utilizzato in questo esempio? Sto cercando di capire lambda e come funziona in questo esempio, ma ho ancora problemi. Ho capito che lambda è come una funzione di riga che può essere passata molto facilmente rispetto alla norma delle funzioni def?
Newbie

def inner: return "<b>" + fn() + "</b>", allora return innersarebbe la versione della funzione "normale"; non una grande differenza.
Martijn Pieters

Mi confondo sempre sull'ordine. "... i decoratori verranno applicati a partire da quello più vicino all'istruzione" def ", che io chiamo" inside-out ". Penso che Martijn lo chiami "esteriore". Ciò significa che make_italic decorator viene eseguito prima di make_bold decorator , perché make_italicè il più vicino al def. Tuttavia, dimentico l' ordine di esecuzione del codice decorato : il lambda make_bold decorato (cioè il lambda in grassetto) viene eseguito per primo, seguito dal lambda make_italic decorato (cioè il lambda in corsivo).
The Red Pea
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.