Ausführungsreihenfolge des Dekorateurs

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()

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

Ich verstehe grob über Dekorateure und wie es mit einem davon in den meisten Beispielen funktioniert.

In diesem Beispiel gibt es zwei davon. Aus der Ausgabe geht hervor, dass diese @make_italiczuerst ausgeführt wird @make_bold.

Bedeutet dies, dass bei dekorierten Funktionen zuerst die Funktion ausgeführt wird und dann bei anderen Dekorateuren nach oben verschoben wird? Wie @make_italiczuerst dann @make_bold, anstatt das Gegenteil.

Das bedeutet also, dass es sich in den meisten Programmiersprachen von der Norm des Top-Down-Ansatzes unterscheidet? Nur für diesen Fall von Dekorateur? Oder liege ich falsch?

Neuling
quelle
4
Ja, es beginnt von unten nach oben und gibt das Ergebnis an das nächste weiter
Padraic Cunningham,
1
@ PadraicCunningham Kommentar ist auch ein wichtiger Teil der Antwort. Hatte ein verwandtes Problem ( stackoverflow.com/questions/47042196/… )
shookees
Ich würde sagen, es ist immer noch von oben nach unten, in dem Sinne, dass a(b(x))es von oben nach unten ist (wenn Sie sich vorstellen, dass es sich um 3 Zeilen handelt)
Joel

Antworten:

124

Dekorateure verpacken die Funktion, die sie dekorieren. So make_bolddekorierte das Ergebnis des make_italicDekorateurs, der die helloFunktion dekorierte .

Die @decoratorSyntax ist wirklich nur syntaktischer Zucker; folgende:

@decorator
def decorated_function():
    # ...

wird wirklich ausgeführt als:

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

Ersetzen des ursprünglichen decorated_functionObjekts durch das decorator()zurückgegebene Objekt .

Das Stapeln von Dekorateuren wiederholt diesen Vorgang nach außen .

Also deine Probe:

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

kann erweitert werden auf:

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

Wenn Sie hello()jetzt aufrufen , rufen Sie das von make_bold()wirklich zurückgegebene Objekt auf . make_bold()hat a zurückgegeben lambda, das die make_boldumschlossene Funktion aufruft. Dies ist der Rückgabewert von make_italic(). Dies ist auch ein Lambda, das das Original aufruft hello(). Wenn Sie all diese Anrufe erweitern, erhalten Sie:

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

so wird die Ausgabe:

"<b>" + ("<i>" + ("hello world") + "</i>") + "</b>"
Martijn Pieters
quelle
Ich verstehe. Bedeutet dies jedoch, dass die IDE bei 2 Wrappern in diesem Fall das Ergebnis des ersten Wrappers automatisch erkennt und umschließt? Weil ich das gedacht habe @make_bold #make_bold = make_bold(hello) @make_italic #make_italic = make_italic (hello)? Ich bin mir nicht sicher, ob das erste Ergebnis darauf basiert. Oder wird für diesen Fall von 2 Wrappern die IDE make_bold(make_italic(hello))wie von Ihnen erwähnt anstelle der von mir freigegebenen verwendet?
Neuling
3
@Newbie: Deine IDE macht hier nichts; Es ist Python , der das Wrapping macht. Ich habe Ihnen in meinem letzten Beispiel gezeigt, dass make_bold()die Ausgabe von make_italic(), die zum Umschließen verwendet wurde hello, das Äquivalent von umschließt make_bold(make_italic(hello)).
Martijn Pieters
Könnten Sie eine Version dieses Codes ohne die Verwendung von Lambda bereitstellen? Ich hatte .format ausprobiert, funktioniert aber nicht. Und warum wird in diesem Beispiel Lambda verwendet? Ich versuche Lambda zu verstehen und wie es in diesem Beispiel funktioniert, habe aber immer noch Probleme. Ich verstehe, dass Lambda wie einzeilige Funktionen ist, die im Vergleich zur Norm der Def-Funktionen sehr einfach übergeben werden können.
Neuling
def inner: return "<b>" + fn() + "</b>", Dann return innerwäre die ‚normale‘ Funktion Version; kein so großer Unterschied.
Martijn Pieters
Ich bin immer verwirrt über die Ordnung. "... Dekorateure werden beginnend mit demjenigen angewendet, der der" def "-Anweisung am nächsten kommt." Ich nenne dies "von innen nach außen". Ich denke, Martijn nennt das "nach außen". Dies bedeutet, dass der make_italic Dekorator vor dem make_bold Dekorator ausgeführt wird , da er dem make_italicam nächsten liegt def. Ich vergesse jedoch die Reihenfolge der Ausführung des dekorierten Codes: Das make_bold dekorierte (dh fette Lambda) wird zuerst ausgeführt, gefolgt vom make_italic dekorierten Lambda (dh dem kursiven Lambda).
Die rote Erbse