Dekoratoren sind ein wichtiger Teil von Python. Einfach ausgedrückt: Es sind Funktionen, die die Funktionalität anderer Funktionen verändern. Sie helfen, unseren Code kürzer und pythonischer zu machen. Die meisten Anfänger wissen nicht, wo sie sie einsetzen sollen, deshalb werde ich einige Bereiche vorstellen, in denen Dekoratoren deinen Code sauberer machen können. Zuerst besprechen wir, wie man seine eigenen Dekoratoren schreibt.

Dies ist wahrscheinlich eines der schwierigsten Konzepte, die man beherrschen muss. Wir werden es Schritt für Schritt besprechen, damit du es vollständig verstehst.

Alles ist ein Objekt

Zuerst wollen wir Funktionen in Python verstehen:

def hi(name="yasoob"): return "hi " + name print(hi()) # Ausgabe: 'hi yasoob' # Wir können sogar eine Funktion einer Variablen zuweisen, zum Beispiel greet = hi # Wir verwenden hier keine Klammern, weil wir die Funktion hi nicht aufrufen # sondern sie vielmehr der Variablen greet zuweisen. Versuchen wir, das auszuführen print(greet()) # Ausgabe: 'hi yasoob' # Wenn wir die alte Funktion hi löschen, sieh, was passiert! del hi print(hi()) #Ausgabe: NameError print(greet()) #Ausgabe: 'hi yasoob'

Funktionen innerhalb von Funktionen definieren

Das waren die Grundlagen von Funktionen. Bringen wir dein Wissen einen Schritt weiter. In Python können wir eine Funktion innerhalb einer anderen Funktion definieren:

def hi(name="yasoob"): print("jetzt bist du in der hi()-Funktion") def greet(): return "jetzt bist du in der greet()-Funktion" def welcome(): return "jetzt bist du in der welcome()-Funktion" print(greet()) print(welcome()) print("jetzt bist du wieder in der hi()-Funktion") hi() #Ausgabe:jetzt bist du in der hi()-Funktion # jetzt bist du in der greet()-Funktion # jetzt bist du in der welcome()-Funktion # jetzt bist du wieder in der hi()-Funktion # Das Obige zeigt, dass, wann immer du hi() aufrufst, greet() und welcome() gleichzeitig aufgerufen werden. # Dann sind die Funktionen greet() und welcome() außerhalb der hi()-Funktion nicht zugänglich, zum Beispiel: greet() #Ausgabe: NameError: name 'greet' is not defined

Jetzt wissen wir, dass wir andere Funktionen innerhalb einer Funktion definieren können. Das heißt: Wir können verschachtelte Funktionen erstellen. Jetzt musst du noch ein wenig mehr lernen: Funktionen können auch Funktionen zurückgeben.

Funktionen aus Funktionen zurückgeben

Eigentlich ist es nicht nötig, eine andere Funktion innerhalb einer Funktion auszuführen; wir können sie auch als Ausgabe zurückgeben:

def hi(name="yasoob"): def greet(): return "jetzt bist du in der greet()-Funktion" def welcome(): return "jetzt bist du in der welcome()-Funktion" if name == "yasoob": return greet else: return welcome a = hi() print(a) #Ausgabe: <function greet at 0x7f2143c01500> # Das oben zeigt deutlich, dass `a` jetzt auf die greet()-Funktion innerhalb der hi()-Funktion verweist # Probiere jetzt dies print(a()) #Ausgabe: jetzt bist du in der greet()-Funktion

Schau dir diesen Code noch einmal an. In der if/else-Anweisung geben wir greet und welcome zurück, nicht greet() und welcome(). Warum ist das so? Das liegt daran, dass die Funktion ausgeführt wird, wenn man ein Klammernpaar dahinter setzt; wenn man jedoch keine Klammern dahinter setzt, kann sie weitergegeben und anderen Variablen zugewiesen werden, ohne dass sie ausgeführt wird. Verstehst du? Lass es mich etwas detaillierter erklären.

  • Wenn wira = hi(),hi() schreiben,wird es ausgeführt, und da der Parameter name standardmäßig yasoob ist, wird die Funktiongreet()zurückgegeben.

  • Wir können auchhi()(), was Folgendes ausgibt:jetzt bist du in der greet()-Funktion。

  • Wenn wir die Anweisung zua = hi(name = "ali")ändern, dannwelcome()wird die Funktion zurückgegeben.

Eine Funktion als Argument an eine andere Funktion übergeben

def hi(): return "hi yasoob!" def doSomethingBeforeHi(func): print("Ich erledige etwas langweilige Arbeit, bevor ich hi() ausführe") print(func()) doSomethingBeforeHi(hi) #outputs:Ich erledige etwas langweilige Arbeit, bevor ich hi() ausführe # hi yasoob!

Jetzt hast du all das nötige Wissen, um zu lernen, was Dekoratoren wirklich sind. Dekoratoren ermöglichen es dir, Code vor und nach einer Funktion auszuführen.

Dein erster Dekorator

Im vorherigen Beispiel haben wir tatsächlich bereits einen Dekorator erstellt! Jetzt lass uns den vorherigen Dekorator ändern und ein etwas nützlicheres Programm schreiben:

def a_new_decorator(a_func): def wrapTheFunction(): print("Ich erledige etwas langweilige Arbeit, bevor ich a_func() ausführe") a_func() print("Ich erledige etwas langweilige Arbeit, nachdem ich a_func() ausgeführt habe") return wrapTheFunction def a_function_requiring_decoration(): print("Ich bin die Funktion, die etwas Dekoration braucht, um meinen üblen Geruch zu entfernen") a_function_requiring_decoration() #Ausgabe: "Ich bin die Funktion, die etwas Dekoration braucht, um meinen üblen Geruch zu entfernen" a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) #jetzt wird a_function_requiring_decoration von wrapTheFunction() umhüllt a_function_requiring_decoration() #Ausgabe: Ich erledige etwas langweilige Arbeit, bevor a_func() ausgeführt wird # Ich bin die Funktion, die etwas Dekoration braucht, um meinen üblen Geruch zu entfernen # Ich erledige etwas langweilige Arbeit, nachdem a_func() ausgeführt wurde

Siehst du es? Wir haben gerade die Prinzipien angewendet, die wir früher gelernt haben. Genau das machen Dekoratoren in Python! Sie umhüllen eine Funktion und ändern ihr Verhalten auf die eine oder andere Weise. Jetzt fragst du dich vielleicht, in unserem Code haben wir das@Symbol nicht verwendet? Das ist nur eine Kurzform, um eine dekorierte Funktion zu erzeugen. So verwenden wir@um den vorherigen Code auszuführen:

@a_new_decorator def a_function_requiring_decoration(): """Hey du! Dekoriere mich!""" print("Ich bin die Funktion, die etwas Dekoration braucht, um " "meinen üblen Geruch zu entfernen") a_function_requiring_decoration() #Ausgabe: Ich erledige etwas langweilige Arbeit, bevor a_func() ausgeführt wird # Ich bin die Funktion, die etwas Dekoration braucht, um meinen üblen Geruch zu entfernen # Ich erledige etwas langweilige Arbeit, nachdem a_func() ausgeführt wurde #das @a_new_decorator ist nur eine Kurzform für: a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

Hoffentlich hast du jetzt ein grundlegendes Verständnis dafür, wie Python-Dekoratoren funktionieren. Es gibt ein Problem, wenn wir den folgenden Code ausführen:

print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction

Das ist nicht, was wir wollten! Die Ausgabe sollte "a_function_requiring_decoration" sein. Hier wurde die Funktion durch warpTheFunction ersetzt. Sie hat den Namen und den Docstring unserer Funktion überschrieben. Glücklicherweise stellt uns Python eine einfache Funktion zur Verfügung, um dieses Problem zu lösen, nämlich functools.wraps. Ändern wir das vorherige Beispiel so, dass es functools.wraps verwendet:

from functools import wraps def a_new_decorator(a_func): @wraps(a_func) def wrapTheFunction(): print("Ich verrichte etwas langweilige Arbeit, bevor ich a_func() ausführe") a_func() print("Ich erledige etwas langweilige Arbeit, nachdem ich a_func() ausgeführt habe") return wrapTheFunction @a_new_decorator def a_function_requiring_decoration(): """Hey yo! Dekoriere mich!""" print("Ich bin die Funktion, die etwas Dekoration braucht, um " "meinen üblen Geruch zu entfernen") print(a_function_requiring_decoration.__name__) # Ausgabe: a_function_requiring_decoration

Das ist jetzt viel besser. Als Nächstes lernen wir einige häufige Anwendungsfälle von Dekoratoren kennen.

Bauplan-Spezifikation:

from functools import wraps def decorator_name(f): @wraps(f) def decorated(*args, **kwargs): if not can_run: return "Funktion wird nicht ausgeführt" return f(*args, **kwargs) return decorated @decorator_name def func(): return("Funktion wird ausgeführt") can_run = True print(func()) # Ausgabe: Funktion wird ausgeführt can_run = False print(func()) # Ausgabe: Funktion wird nicht ausgeführt

Hinweis:@wrapsSie akzeptiert eine zu dekorierende Funktion und fügt Funktionalität hinzu, um den Funktionsnamen, Docstring, die Argumentliste und so weiter zu kopieren. Dadurch können wir innerhalb des Dekorators auf die Attribute der Funktion vor der Dekoration zugreifen.


Anwendungsfälle

Schauen wir uns nun an, wo Dekoratoren wirklich glänzen, und wie ihre Verwendung die Verwaltung einiger Dinge erleichtert.

Authorization

Dekoratoren können dabei helfen zu prüfen, ob jemand berechtigt ist, einen Endpunkt einer Webanwendung zu verwenden. Sie werden in den Web-Frameworks Flask und Django stark genutzt. Hier ist ein Beispiel für die Verwendung einer dekoratorbasierten Autorisierung:

from functools import wraps def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): authenticate() return f(*args, **kwargs) return decorated

Protokollierung

Logging ist ein weiteres Highlight bei der Verwendung von Dekoratoren. Hier ist ein Beispiel:

from functools import wraps def logit(func): @wraps(func) def with_logging(*args, **kwargs): print(func.__name__ + " wurde aufgerufen") return func(*args, **kwargs) return with_logging @logit def addition_func(x): """Berechne etwas.""" return x + x result = addition_func(4) # Ausgabe: addition_func wurde aufgerufen

Ich bin mir sicher, dass du bereits an eine weitere clevere Verwendung von Dekoratoren denkst.


Dekoratoren mit Parametern

Denke darüber nach: Ist @wraps nicht auch ein Dekorator? Aber er nimmt einen Parameter, genau wie jede gewöhnliche Funktion es kann. Warum machen wir das also nicht auch? Das liegt daran, dass du, wenn du die Syntax @my_decorator verwendest, eine Wrapper-Funktion anwendest, die eine einzelne Funktion als Argument nimmt. Denke daran: Alles in Python ist ein Objekt, und das schließt Funktionen ein! Mit diesem Gedanken können wir eine Funktion schreiben, die eine Wrapper-Funktion zurückgibt.

Dekoratoren in Funktionen einbetten

Kehren wir zum Logging-Beispiel zurück und erstellen eine Wrapper-Funktion, mit der wir eine Logdatei für die Ausgabe angeben können.

from functools import wraps def logit(logfile='out.log'): def logging_decorator(func): @wraps(func) def wrapped_function(*args, **kwargs): log_string = func.__name__ + " wurde aufgerufen" print(log_string) # Logdatei öffnen und Inhalt schreiben with open(logfile, 'a') as opened_file: # Jetzt in die angegebene Logdatei schreiben opened_file.write(log_string + '\n') return func(*args, **kwargs) return wrapped_function return logging_decorator @logit() def myfunc1(): pass myfunc1() # Ausgabe: myfunc1 wurde aufgerufen # Jetzt erscheint eine Datei namens out.log, und ihr Inhalt ist der obige String @logit(logfile='func2.log') def myfunc2(): pass myfunc2() # Ausgabe: myfunc2 wurde aufgerufen # Jetzt erscheint eine Datei namens func2.log, und ihr Inhalt ist der obige String

Dekorator-Klassen

Jetzt haben wir einen logit-Dekorator, der für die Produktion geeignet ist, aber wenn bestimmte Teile unserer Anwendung noch fragil sind, können Ausnahmen eine dringendere Aufmerksamkeit erfordern. Zum Beispiel möchten Sie manchmal nur in eine Datei protokollieren. Andere Male möchten Sie ein Problem, das Ihre Aufmerksamkeit erregt, per E-Mail senden und gleichzeitig ein Protokoll für die Aufzeichnung führen. Dies ist ein Szenario für die Verwendung von Vererbung, aber bisher haben wir nur Funktionen gesehen, die zum Erstellen von Dekoratoren verwendet werden.

Glücklicherweise können auch Klassen verwendet werden, um Dekoratoren zu erstellen. Lassen Sie uns also jetzt logit mithilfe einer Klasse statt einer Funktion neu aufbauen.

from functools import wraps class logit(object): def __init__(self, logfile='out.log'): self.logfile = logfile def __call__(self, func): @wraps(func) def wrapped_function(*args, **kwargs): log_string = func.__name__ + " wurde aufgerufen" print(log_string) # Logdatei öffnen und schreiben with open(self.logfile, 'a') as opened_file: # Nun in die angegebene Datei protokollieren opened_file.write(log_string + '\n') # Nun eine Benachrichtigung senden self.notify() return func(*args, **kwargs) return wrapped_function def notify(self): # logit protokolliert nur, tut sonst nichts pass

Diese Implementierung hat den zusätzlichen Vorteil, sauberer zu sein als der Ansatz mit verschachtelter Funktion, und das Umwickeln einer Funktion verwendet weiterhin die gleiche Syntax wie zuvor:

@logit()
def myfunc1():
    pass

Jetzt erstellen wir eine Unterklasse von logit, um E-Mail-Funktionalität hinzuzufügen (obwohl das Thema E-Mail hier nicht weiter ausgeführt wird).

class email_logit(logit): '''Eine logit-Implementierungsversion, die eine E-Mail an den Administrator senden kann, wenn die Funktion aufgerufen wird''' def __init__(self, email='admin@myproject.com', *args, **kwargs): self.email = email super(email_logit, self).__init__(*args, **kwargs) def notify(self): # Eine E-Mail an self.email senden # Wir werden dies hier nicht implementieren pass

Ab jetzt hat @email_logit denselben Effekt wie @logit, aber zusätzlich zum Logging wird auch eine E-Mail an den Administrator gesendet.

Originale Artikeladresse: https://eastlakeside.gitbooks.io/interpy-zh/content/decorators/