Mythos der Ausführungsreihenfolge des Python-Dekorators

高洛峰
Freigeben: 2017-02-17 11:02:44
Original
1019 Leute haben es durchsucht

Untersuchen der Ausführungsreihenfolge mehrerer Dekoratoren

Dekoratoren sind Tools, die von Python zum Kapseln von Funktionen oder Codes verwendet werden. Im Internet finden Sie viele Artikel, aus denen ich lernen möchte mehrere Dekorateure. Ein Mythos über die Reihenfolge.

Fragen

Die meisten Funktionsaufrufsequenzen mit mehreren Dekoratoren zeigen an, dass sie von oben nach unten erfolgen, wie zum Beispiel das folgende Beispiel:

def decorator_a(func):
    print 'Get in decorator_a'
    def inner_a(*args, **kwargs):
        print 'Get in inner_a'
        return func(*args, **kwargs)
    return inner_a

def decorator_b(func):
    print 'Get in decorator_b'
    def inner_b(*args, **kwargs):
        print 'Get in inner_b'
        return func(*args, **kwargs)
    return inner_b

@decorator_b
@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

f(1)
Nach dem Login kopieren

Der obige Code zuerst definiert zwei Funktionen: decotator_a, decotator_b. Die Funktion dieser beiden Funktionen besteht darin, eine Funktion als Parameter zu empfangen und dann eine andere erstellte Funktion aufzurufen (der Text ist komplizierter als der Code). ). Die zuletzt definierte Funktion f verwendet decotator_a und decotator_b, die oben als Dekorationsfunktionen definiert wurden. Nachdem wir die dekorierte Funktion f mit 1 als Parameter aufgerufen haben, wie ist die Reihenfolge von decotator_a und decotator_b (hier wird die Druckausgabe verwendet, um die Reihenfolge der Funktionsausführung anzuzeigen, um die Reihenfolge der Funktionsausführung anzuzeigen)?

Wenn Sie ohne nachzudenken nach dem Bottom-up-Prinzip urteilen, führen Sie zuerst decorator_a und dann decorator_b aus, dann wird zuerst Get in decorator_a, Get in inner_a und dann Get in decorator_b, Get in inner_b ausgegeben. Dies ist jedoch nicht der Fall.

Die tatsächlichen Ausführungsergebnisse sind wie folgt:

Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f
Nach dem Login kopieren

Der Unterschied zwischen Funktionen und Funktionsaufrufen

Warum wird inner_b zuerst und dann inner_a ausgeführt? Um das obige Problem vollständig zu verstehen, müssen wir zunächst zwischen zwei Konzepten unterscheiden: Funktionen und Funktionsaufrufe. Im obigen Beispiel wird f als Funktion und f(1) als Funktionsaufruf bezeichnet. Letzteres ist das Ergebnis der Auswertung der von ersterem übergebenen Parameter. In Python ist eine Funktion auch ein Objekt, daher bezieht sich f auf ein Funktionsobjekt, und sein Wert ist die Funktion selbst. f(1) ist ein Aufruf der Funktion, und sein Wert ist das Ergebnis des Aufrufs Definition hier, f(1) Der Wert ist 2. In ähnlicher Weise gibt sie am Beispiel der Funktion decorator_a oben ein Funktionsobjekt inner_a zurück, das intern definiert ist. Die Funktion func wird in inner_a aufgerufen und das Ergebnis des Aufrufs von func wird als Wert zurückgegeben.

Die Dekoratorfunktion wird unmittelbar nach der Definition der dekorierten Funktion ausgeführt

Die zweite Frage, die geklärt werden muss, ist, was genau passiert, wenn der Dekorator eine Funktion dekoriert. Vereinfachen Sie nun unser Beispiel und gehen Sie davon aus, dass es wie folgt lautet:

def decorator_a(func):
    print 'Get in decorator_a'
    def inner_a(*args, **kwargs):
        print 'Get in inner_a'
        return func(*args, **kwargs)
    return inner_a

@decorator_a
def f(x):
    print 'Get in f'
    return x * 2
Nach dem Login kopieren

Wie in vielen Artikeln zur Einführung von Dekoratoren gesagt:

@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

# 相当于
def f(x):
    print 'Get in f'
    return x * 2

f = decorator_a(f)
Nach dem Login kopieren

Also, wenn der Interpreter diesen Code ausführt, wenn decorator_a war aufgerufen, nimmt es die Funktion f als Parameter und gibt eine intern generierte Funktion zurück, sodass f sich danach auf inner_a bezieht, das in decorator_a zurückgegeben wird. Wenn also f in der Zukunft aufgerufen wird, entspricht dies tatsächlich dem Aufruf von inner_a. Die an f übergebenen Parameter werden an inner_a übergeben, und die empfangenen Parameter werden an die Funktion in inner_a übergeben Die endgültige Rückgabe ist der von f aufgerufene Wert, sodass es nach außen hin so aussieht, als würde f direkt erneut aufgerufen.

Erläuterung der Fragen

Wenn die beiden oben genannten Konzepte geklärt sind, können Sie deutlich sehen, was im originellsten Beispiel passiert ist.
Wenn der Interpreter den folgenden Code ausführt, ruft er tatsächlich decorator_a und decorator_b in der Reihenfolge von unten nach oben auf, wodurch das entsprechende Get in decorator_a und Get in decorator_b ausgegeben wird. Zu diesem Zeitpunkt entspricht f inner_b in decorator_b. Da jedoch f nicht aufgerufen wurde, wurde inner_b nicht aufgerufen, und analog dazu wurde inner_a innerhalb von inner_b nicht aufgerufen, sodass „Get in inner_a“ und „Get in inner_b“ nicht ausgegeben werden.

@decorator_b
@decorator_a
def f(x):
    print 'Get in f'
    return x * 2
Nach dem Login kopieren

Wenn wir dann in der letzten Zeile f mit Parameter 1 aufrufen, wird inner_b aufgerufen. Zuerst wird Get in inner_b ausgegeben und dann inner_a innerhalb von inner_b aufgerufen, sodass Get in erneut gedruckt wird. inner_a, rufen Sie dann das ursprüngliche f intern in inner_a auf und geben Sie das Ergebnis als Endergebnis zurück. An diesem Punkt sollten Sie wissen, warum die Ausgabe so ist, und ein gewisses Verständnis dafür haben, was tatsächlich in der Ausführungssequenz des Dekorators passiert.

Wenn wir den Aufruf von f in der letzten Zeile des obigen Beispiels entfernen und ihn zur Demonstration in die Antwort einfügen, können wir natürlich auch das Reihenfolgeproblem erkennen:

➜  test git:(master) ✗ python
Python 2.7.11 (default, Jan 22 2016, 08:29:18)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import test13
Get in decorator_a
Get in decorator_b
>>> test13.f(1)
Get in inner_b
Get in inner_a
Get in f
2
>>> test13.f(2)
Get in inner_b
Get in inner_a
Get in f
4
>>>
Nach dem Login kopieren

In Wirklichkeit Anwendungsszenarien: Wenn wir auf die oben beschriebene Weise zwei Dekorationsmethoden schreiben, z. B. zuerst überprüfen, ob ein Login @login_required vorhanden ist, und dann überprüfen, ob die Berechtigungen @permision_allowed ausreichen, verwenden wir die folgende Reihenfolge, um die Funktion zu dekorieren:

@login_required
@permision_allowed
def f()
  # Do something
  return
Nach dem Login kopieren

Weitere Artikel zu den Mythen über die Ausführungsreihenfolge von Python-Dekoratoren finden Sie auf der chinesischen PHP-Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!