Python Decorators
Table of Contents
Ein Decorator nimmt eine Funktion auf, fügt einige Funktionen hinzu und gibt sie zurück. In diesem Lernprogramm erfahren Sie, wie Sie einen Decorator erstellen können und warum Sie ihn verwenden sollten.
Decorators in Python
Python hat eine interessante Funktion namens Decorators, um einem bestehenden Code Funktionalität hinzuzufügen.
Dies wird auch als Metaprogrammierung bezeichnet, da ein Teil des Programms versucht, einen anderen Teil des Programms zur Kompilierzeit zu verändern.
Prerequisites for learning decorators
Um Dekoratoren zu verstehen, müssen wir zunächst ein paar grundlegende Dinge in Python wissen.
Wir müssen uns mit der Tatsache anfreunden, dass alles in Python (Ja! Sogar Klassen), Objekte sind. Namen, die wir definieren, sind einfach Bezeichner, die an diese Objekte gebunden sind. Funktionen sind da keine Ausnahme, auch sie sind Objekte (mit Attributen). An ein und dasselbe Funktionsobjekt können verschiedene Namen gebunden werden.
Hier ist ein Beispiel.
def first(msg): print(msg) first("Hello") second = first second("Hello")
Output
Hello Hello
Wenn Sie den Code ausführen, werden beide Funktionen first
und second
ergeben die gleiche Ausgabe. Hier sind die Namen first
und second
beziehen sich auf das gleiche Funktionsobjekt.
Jetzt werden die Dinge etwas merkwürdiger.
Funktionen können als Argumente an eine andere Funktion übergeben werden.
Wenn Sie Funktionen wie map
, filter
und reduce
in Python, dann wissen Sie das bereits.
Solche Funktionen, die andere Funktionen als Argumente annehmen, werden auch als higher order functions. Hier ist ein Beispiel für eine solche Funktion.
def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result
Wir rufen die Funktion wie folgt auf.
>>> operate(inc,3) 4 >>> operate(dec,3) 2
Außerdem kann eine Funktion eine andere Funktion zurückgeben.
def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() # Outputs "Hello" new()
Output
Hello
Hier, is_returned()
ist eine nested Funktion, die bei jedem Aufruf definiert und zurückgegeben wird is_called()
.
Schließlich müssen wir noch etwas über Closures in Python wissen.
Getting back to Decorators
Funktionen und Methoden werden als callable bezeichnet, da sie aufgerufen werden können.
In der Tat kann jedes Objekt, das die spezielle __call__()
Methode wird als callable bezeichnet. Im einfachsten Sinne ist ein Dekorator also ein Callable, das ein Callable zurückgibt.
Im Grunde genommen nimmt ein Decorator eine Funktion auf, fügt einige Funktionen hinzu und gibt sie zurück.
def make_pretty(func): def inner(): print("Ich wurde dekoriert") func() return inner def ordinary(): print("Ich bin gewöhnlich")
When you run the following codes in shell,
>>> ordinary() I am ordinary >>> # Lassen Sie uns diese gewöhnliche Funktion decorate >>> pretty = make_pretty(ordinary) >>> pretty() Ich wurde dekoriert Ich bin gewöhnlich
Im oben gezeigten Beispiel, make_pretty()
ist ein Decorator. Im Schritt der Zuweisung:
pretty = make_pretty(ordinary)
Die Funktion ordinary()
wurde dekoriert und die zurückgegebene Funktion erhielt den Namen pretty
.
Wir können sehen, dass die Dekoratorfunktion der ursprünglichen Funktion einige neue Funktionen hinzugefügt hat. Dies ist ähnlich wie das Verpacken eines Geschenks. Der Dekorator fungiert als Hülle. Die Natur des Objekts, das dekoriert wurde (das eigentliche Geschenk darin), ändert sich nicht. Aber es sieht jetzt hübsch aus (da es dekoriert wurde).
Im Allgemeinen dekorieren wir eine Funktion und weisen sie neu zu als,
ordinary = make_pretty(ordinary).
Dies ist ein gängiges Konstrukt und aus diesem Grund gibt es in Python eine Syntax, die dies vereinfacht.
Wir können die @
Symbol zusammen mit dem Namen der Dekoratorfunktion und platzieren Sie es oberhalb der Definition der zu dekorierenden Funktion. Zum Beispiel,
@make_pretty def ordinary(): print("Ich bin gewöhnlich")
ist äquivalent zu
def ordinary(): print("Ich bin gewöhnlich") ordinary = make_pretty(ordinary)
Dies ist nur ein syntaktischer Zucker, um Dekoratoren zu implementieren.
Decorating Functions with Parameters
Der obige Decorator war einfach und er funktionierte nur mit Funktionen, die keine Parameter hatten. Was wäre, wenn wir Funktionen hätten, die Parameter aufnehmen, wie z. B.:
def divide(a, b): return a/b
Diese Funktion hat zwei Parameter, a und b. Wir wissen, dass es einen Fehler geben wird, wenn wir in b as 0.
>>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last): ... ZeroDivisionError: division by zero
Lassen Sie uns nun einen Decorator erstellen, der auf diesen Fall prüft, der den Fehler verursacht
def smart_divide(func): def inner(a, b): print("Divide", a, "und", b) if b == 0: print("kann nicht divide") return return func(a, b) return inner @smart_divide def divide(a, b): print(a/b)
Diese neue Implementierung liefert None
wenn die Fehlerbedingung auftritt.
>>> divide(2,5) Divide 2 und 5 0.4 >>> divide(2,0) Divide 2 und 0 kann nicht divide
Auf diese Weise können wir Funktionen, die Parameter annehmen, dekorieren.
Einem aufmerksamen Beobachter wird auffallen, dass die Parameter der nested inner()
Funktion innerhalb des Dekorators ist die gleiche wie die Parameter der Funktionen, die er dekoriert. Wenn wir dies berücksichtigen, können wir nun allgemeine Dekoratoren erstellen, die mit einer beliebigen Anzahl von Parametern arbeiten.
In Python wird diese Magie wie folgt ausgeführt function(*args, **kwargs)
. Auf diese Weise, args
wird das Tupel der Positionsargumente sein und kwargs
wird das Wörterbuch der Schlüsselwortargumente sein. Ein Beispiel für einen solchen Dekorator ist:
def works_for_all(func): def inner(*args, **kwargs): print("Ich kann jede Funktion dekorieren") return func(*args, **kwargs) return inner
Chaining Decorators in Python
In Python können mehrere Decorators verkettet werden.
Das heißt, eine Funktion kann mehrfach mit verschiedenen (oder gleichen) Decorators dekoriert werden. Wir platzieren die Decorators einfach oberhalb der gewünschten Funktion.
def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%" * 30) func(*args, **kwargs) print("%" * 30) return inner @star @percent def printer(msg): print(msg) printer("Hello")
Output
****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************
Die obige Syntax von,
@star @percent def printer(msg): print(msg)
ist äquivalent zu
def printer(msg): print(msg) printer = star(percent(printer))
Die Reihenfolge, in der wir Dekoratoren verketten, ist wichtig. Wenn wir die Reihenfolge umgedreht hätten als,
@percent @star def printer(msg): print(msg)
Die Ausgabe würde sein:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%