Python-Dekoratoren sind ein unglaublich nützliches Konstrukt in Python. Mit Dekoratoren in Python können wir das Verhalten einer Funktion ändern, indem wir sie in eine andere Funktion einpacken. Mit Dekoratoren können wir sauberen Code schreiben und Funktionen gemeinsam nutzen. In diesem Artikel lernen Sie nicht nur, wie man Dekoratoren verwendet, sondern auch, wie man sie erstellt.
Vorausgesetztes Wissen
Das Thema Dekoratoren in Python erfordert etwas Hintergrundwissen. In der Folge habe ich einige Konzepte aufgeführt, mit denen Sie bereits vertraut sein sollten, um dieses Tutorial sinnvoll zu nutzen. Ich habe auch Ressourcen verlinkt, in denen Sie die Konzepte bei Bedarf auffrischen können.
Grundlagen von Python
Dieses Thema ist ein Thema für Fortgeschrittene. Daher sollten Sie bereits mit den Grundlagen von Python vertraut sein, z.B. mit Datentypen, Funktionen, Objekten und Klassen, bevor Sie versuchen, es zu lernen.
Sie sollten auch einige objektorientierte Konzepte wie Getter, Setter und Konstruktoren verstehen. Wenn Sie mit der Programmiersprache Python nicht vertraut sind, finden Sie hier einige Ressourcen, die Ihnen den Einstieg erleichtern erleichtern.
Funktionen sind Bürger erster Klasse
Zusätzlich zu den Grundkenntnissen in Python sollten Sie auch dieses fortgeschrittenere Konzept in Python kennen. Funktionen, und so ziemlich alles andere in Python, sind Objekte wie int
oder string
. Da es sich um Objekte handelt, können Sie einige Dinge mit ihnen tun, und zwar:
- Sie können eine Funktion als Argument an eine andere Funktion übergeben, genauso wie Sie eine
String
oderint
als Funktionsargument übergeben. - Funktionen können auch von anderen Funktionen zurückgegeben werden, so wie Sie andere
String-
oderint-Werte
zurückgeben würden. - Funktionen können in Variablen gespeichert werden
Tatsächlich besteht der einzige Unterschied zwischen funktionalen Objekten und anderen Objekten darin, dass funktionale Objekte die magische Methode __call__()
enthalten.
Wir hoffen, dass Sie an dieser Stelle mit dem vorausgesetzten Wissen vertraut sind. Wir können mit der Diskussion des Hauptthemas beginnen.
Was ist ein Python-Dekorator?
Ein Python-Dekorator ist einfach eine Funktion, die eine Funktion als Argument entgegennimmt und eine modifizierte Version der übergebenen Funktion zurückgibt. Mit anderen Worten: Die Funktion foo ist ein Dekorator, wenn sie als Argument die Funktion bar
entgegengenommen und eine andere Funktion baz
zurückgibt.
Die Funktion baz
ist eine Modifikation von bar
in dem Sinne, dass im Körper von baz
ein Aufruf der Funktion bar
erfolgt. Vor und nach dem Aufruf von bar
kann baz
jedoch alles tun. Das war ein ganz schöner Brocken; hier ist etwas Code, um die Situation zu veranschaulichen:
# Foo ist ein Dekorator, er nimmt eine andere Funktion, bar, als Argument auf
def foo(bar):
# Hier erzeugen wir baz, eine modifizierte Version von bar
# baz ruft bar auf, kann aber vor und nach dem Funktionsaufruf alles tun
def baz():
# Bevor wir bar aufrufen, drucken wir etwas
print("Etwas")
# Dann führen wir bar aus, indem wir einen Funktionsaufruf machen
bar()
# Dann drucken wir etwas anderes, nachdem wir bar ausgeführt haben
print("Etwas anderes")
# Schließlich gibt foo baz zurück, eine modifizierte Version von bar
return baz
Wie erstellt man einen Dekorator in Python?
Um zu veranschaulichen, wie Dekoratoren in Python erstellt und verwendet werden, werde ich dies anhand eines einfachen Beispiels erläutern. In diesem Beispiel werden wir eine Logger-Dekoratorfunktion erstellen, die den Namen der Funktion, die sie dekoriert, jedes Mal protokolliert, wenn diese Funktion ausgeführt wird.
Um zu beginnen, haben wir die Dekorator-Funktion erstellt. Der Dekorator nimmt func
als Argument dagegen. func
ist die Funktion, die wir dekorieren.
def create_logger(func):
# Der Funktionskörper kommt hier hin
Innerhalb der Decorator-Funktion erstellen wir unsere modifizierte Funktion, die den Namen von func
protokolliert, bevor wir func
ausführen.
# Innerhalb von create_logger
def modified_func():
print("Aufruf: ", func.__name__)
func()
Anschließend gibt es die Funktion create_logger
die geänderte Funktion zurück. Unsere gesamte Funktion create_logger
sieht dann wie folgt aus:
def create_logger(func):
def modified_func():
print("Calling: ", func.__name__)
func()
return modified_function
Wir sind mit der Erstellung des Dekorators fertig. Die Funktion create_logger
ist ein einfaches Beispiel für eine Decorator-Funktion. Sie nimmt func
auf, die Funktion, die wir dekorieren, und gibt eine andere Funktion, modified_func
, zurück. modified_func
protokolliert zunächst den Namen von func
, bevor func
ausgeführt wird.
Wie man Dekoratoren in Python verwendet
Um unseren Dekorator zu verwenden, benutzen wir die @-Syntax
wie folgt:
@create_logger
def say_hello():
print("Hallo, Welt!")
Jetzt können wir say_hello() in unserem Skript aufrufen, und die Ausgabe sollte folgendermaßen aussehen:
Aufruf von: say_hello
"Hallo, Welt"

Aber was macht der @create_logger
? Nun, er wendet den Dekorator auf unsere say_hello-Funktion an. Um besser zu verstehen, was er tut, würde der Code direkt unter diesem Absatz das gleiche Ergebnis erzielen, wenn Sie @create_logger
vor say_hello
einfügen.
def say_hello():
print("Hallo, Welt!")
say_hello = create_logger(say_hello)
Mit anderen Worten: Eine Möglichkeit, Dekoratoren in Python zu verwenden, besteht darin, den Dekorator explizit aufzurufen und die Funktion zu übergeben, wie wir es im obigen Code getan haben. Der andere und prägnantere Weg ist die Verwendung der @-Syntax
.
In diesem Abschnitt haben wir behandelt, wie man Python-Dekoratoren erstellt.
Etwas kompliziertere Beispiele
Das obige Beispiel war ein einfacher Fall. Es gibt etwas komplexere Beispiele, z.B. wenn die Funktion, die wir dekorieren, Argumente enthält. Eine andere, kompliziertere Situation ist, wenn Sie eine ganze Klasse dekorieren möchten. Ich werde diese beiden Situationen hier behandeln.
Wenn die Funktion Argumente enthält
Wenn die Funktion, die Sie dekorieren, Argumente enthält, sollte die geänderte Funktion die Argumente erhalten und sie weitergeben, wenn sie schließlich die unveränderte Funktion aufruft. Wenn sich das verwirrend anhört, lassen Sie es mich mit den Worten von foo erklären.
Erinnern Sie sich daran, dass foo
die Dekorfunktion ist, bar
ist die Funktion, die wir dekorieren und baz
ist die dekorierte bar
. In diesem Fall nimmt bar die Argumente auf und gibt sie an baz
während des Aufrufs von baz
. Hier ist ein Codebeispiel, um das Konzept zu verdeutlichen:
def foo(bar):
def baz(*args, **kwargs):
# Sie können hier etwas tun
___
# Dann machen wir den Aufruf von bar und übergeben args und kwargs
bar(*args, **kwargs)
# Sie können hier auch etwas tun
___
return baz
Falls Sie *args
und **kwargs
ungewohnt vorkommen, handelt es sich dabei einfach um Zeiger auf die Positions- bzw. Schlüsselwortargumente.
Es ist wichtig zu wissen, dass baz
Zugriff auf die Argumente hat und daher vor dem Aufruf von bar
eine Überprüfung der Argumente durchführen kann.
Ein Beispiel wäre eine Dekorator-Funktion, ensure_string
die sicherstellt, dass das Argument, das an eine Funktion übergeben wird, die sie dekoriert, eine Zeichenkette ist; wir würden sie wie folgt implementieren
def ensure_string(func):
def decorated_func(text):
if type(text) is not str:
raise TypeError('argument to ' func.__name__ ' must be a string.')
else:
func(text)
return decorated_func
Wir könnten die Funktion say_hello
wie folgt dekorieren:
@ensure_string
def say_hello(name):
print('Hallo', name)
Dann könnten wir den Code wie folgt testen:
say_hello('John') # Sollte problemlos laufen
say_hello(3) # Sollte eine Ausnahme auslösen
Und es sollte die folgende Ausgabe erzeugen:
Hallo John
Traceback (letzter Aufruf):
File "/home/anesu/Documents/python-tutorial/./decorators.py", line 20, in <module> say hello(3) # should throw an exception
File "/home/anesu/Documents/python-tu$ ./decorators.pytorial/./decorators.py", line 7, in decorated_func raise TypeError('argument to func._name_ must be a string.')
TypeError: argument to say hello must be a string. $0

Wie erwartet, konnte das Skript 'Hallo John' ausgeben, da 'John' eine Zeichenkette ist. Beim Versuch, 'Hallo 3' zu drucken, wurde eine Ausnahme ausgelöst, da '3' keine Zeichenkette war. Der Dekorator ensure_string
kann verwendet werden, um die Argumente jeder Funktion, die einen String benötigt, zu überprüfen.
Eine Klasse dekorieren
Neben der Dekoration von Funktionen können wir auch Klassen dekorieren. Wenn Sie einer Klasse einen Dekorator hinzufügen, ersetzt die dekorierte Methode die Konstruktor/Initiator-Methode (__init__) der Klasse.
Nehmen wir an, foo ist unser Dekorator und Bar ist die Klasse, die wir dekorieren, dann wird foo Bar.__init__ dekorieren. Dies ist nützlich, wenn wir etwas tun wollen, bevor Objekte vom Typ Bar
instanziiert werden.
Das bedeutet, dass der folgende Code
def foo(func):
def new_func(*args, **kwargs):
print('Etwas vor der Instanziierung tun')
func(*args, **kwargs)
return new_func
@foo
class Bar:
def __init__(self):
print("Im Initiator")
Ist äquivalent zu
def foo(func):
def new_func(*args, **kwargs):
print('Einige Dinge vor der Instanziierung tun')
func(*args, **kwargs)
return new_func
class Bar:
def __init__(self):
print("Im Initiator")
Bar.__init__ = foo(Bar.__init__)
Wenn Sie ein Objekt der Klasse Bar instanziieren, das mit einer der beiden Methoden definiert wurde, sollten Sie die gleiche Ausgabe erhalten:
Einige Dinge vor der Instanziierung tun
In initiator

Beispiele für Dekoratoren in Python
Sie können zwar Ihre eigenen Dekoratoren definieren, aber es gibt auch einige, die bereits in Python eingebaut sind. Hier sind einige der üblichen Dekoratoren, denen Sie in Python begegnen können:
@staticmethod
Die statische Methode wird in einer Klasse verwendet, um anzuzeigen, dass die Methode, die sie dekoriert, eine statische Methode ist. Statische Methoden sind Methoden, die ausgeführt werden können, ohne dass die Klasse instanziert werden muss. Im folgenden Codebeispiel erstellen wir eine Klasse Dog
mit einer statischen Methode bark
.
class Hund:
@staticmethod
def bark():
print('Wuff, wuff!')
Jetzt kann die Methode bellen
wie folgt aufgerufen werden:
Hund.bellen()
Wenn Sie den Code ausführen, erhalten Sie die folgende Ausgabe:
Wuff, wuff!

Wie ich bereits im Abschnitt über die Verwendung von Dekoratoren erwähnt habe, können Dekoratoren auf zwei Arten verwendet werden. Die @-Syntax
ist die prägnantere der beiden Möglichkeiten. Die andere Methode besteht darin, die Decorator-Funktion aufzurufen und dabei die Funktion, die wir dekorieren möchten, als Argument zu übergeben. Das bedeutet, dass der obige Code das Gleiche erreicht wie der folgende Code:
class Dog:
def bark():
print('Wuff, wuff!')
Dog.bark = staticmethod(Dog.bark)
Und wir können die Methode bellen
immer noch auf die gleiche Weise verwenden
Hund.bellen()
Und es würde die gleiche Ausgabe erzeugen
Wuff, wuff!

Wie Sie sehen, ist die erste Methode sauberer und es ist offensichtlicher, dass es sich um eine statische Funktion handelt, bevor Sie überhaupt angefangen haben, den Code zu lesen. Aus diesem Grund werde ich für die verbleibenden Beispiele die erste Methode verwenden. Aber vergessen Sie nicht, dass die zweite Methode eine Alternative ist.
@classmethod
Dieser Dekorator wird verwendet, um anzuzeigen, dass die Methode, die er dekoriert, eine Klassenmethode ist. Klassenmethoden sind statischen Methoden insofern ähnlich, als dass sie beide nicht erfordern, dass die Klasse instanziiert wird, bevor sie aufgerufen werden können.
Der Hauptunterschied besteht jedoch darin, dass Klassenmethoden Zugriff auf Klassenattribute haben, während statische Methoden dies nicht können. Das liegt daran, dass Python die Klasse automatisch als erstes Argument an eine Klassenmethode übergibt, wenn diese aufgerufen wird. Um eine Klassenmethode in Python zu erstellen, können wir den Dekorator classmethod
verwenden.
class Hund:
@classmethod
def what_are_you(cls):
print("Ich bin ein " cls.__name__ "!")
Um den Code auszuführen, rufen wir einfach die Methode auf, ohne die Klasse zu instanziieren:
Dog.what_are_you()
Und die Ausgabe ist:
Ich bin ein Hund!

@Eigenschaft
Der Eigenschaftsdekorator wird verwendet, um eine Methode als Eigenschaftssetzer zu kennzeichnen. Kehren wir zu unserem Beispiel Hund zurück und erstellen wir eine Methode, die den Namen des Hundes abruft.
klasse Hund:
# Erstellen einer Konstruktormethode, die den Namen des Hundes aufnimmt
def __init__(self, name):
# Erstellen einer privaten Eigenschaft name
# Die doppelten Unterstriche machen das Attribut privat
self.__name = name
@property
def name(self):
return self.__name
Jetzt können wir auf den Namen des Hundes wie auf eine normale Eigenschaft zugreifen,
# Erstellen einer Instanz der Klasse
foo = Hund('foo')
# Zugriff auf die Eigenschaft name
print("Der Name des Hundes ist:", foo.name)
Und das Ergebnis der Ausführung des Codes wäre
Der Name des Hundes ist: foo

@property.setter
Der Dekorator property.setter wird verwendet, um eine Setter-Methode für unsere Eigenschaften zu erstellen. Um den @property
.setter-Dekorator zu verwenden, ersetzen Sie property
durch den Namen der Eigenschaft, für die Sie einen Setter erstellen möchten. Wenn Sie zum Beispiel einen Setter für die Methode der Eigenschaft foo erstellen, lautet Ihr Dekorator @foo.setter
. Zur Veranschaulichung hier ein Beispiel für Dog:
class Dog:
# Erstellen einer Konstruktormethode, die den Namen des Hundes aufnimmt
def __init__(self, name):
# Erstellen einer privaten Eigenschaft name
# Die doppelten Unterstriche machen das Attribut privat
self.__name = name
@property
def name(self):
return self.__name
# Erstellen eines Setters für unsere Eigenschaft name
@name.setter
def name(self, new_name):
self.__name = new_name
Um den Setter zu testen, können wir den folgenden Code verwenden:
# Erstellen eines neuen Hundes
foo = Dog('foo')
# Ändern des Hundenamens
foo.name = 'bar'
# Drucken des Hundenamens auf dem Bildschirm
print("Der neue Name des Hundes ist:", foo.name)
Wenn Sie den Code ausführen, erhalten Sie die folgende Ausgabe:
Der neue Name des Hundes ist: bar

Die Bedeutung von Dekoratoren in Python
Nachdem wir nun erklärt haben, was Dekoratoren sind, und Sie einige Beispiele für Dekoratoren gesehen haben, können wir nun erörtern, warum Dekoratoren in Python wichtig sind. Dekoratoren sind aus mehreren Gründen wichtig. Einige davon habe ich im Folgenden aufgeführt:
- Sie ermöglichen die Wiederverwendbarkeit von Code: In dem oben genannten Beispiel für die
Protokollierung
könnten wir den @create_logger für jede beliebige Funktion verwenden. Auf diese Weise können wir allen unseren Funktionen Protokollierungsfunktionen hinzufügen, ohne sie manuell für jede Funktion schreiben zu müssen. - Sie ermöglichen es Ihnen, modularen Code zu schreiben: Um noch einmal auf das Beispiel der Protokollierung zurückzukommen: Mit Dekoratoren können Sie die Kernfunktion, in diesem Fall
say_hello
von den anderen benötigten Funktionen, in diesem Fall der Protokollierung, trennen. - Sie verbessern Frameworks und Bibliotheken: Dekoratoren werden in Python-Frameworks und -Bibliotheken ausgiebig verwendet, um zusätzliche Funktionen bereitzustellen. In Web-Frameworks wie Flask oder Django werden Dekoratoren zum Beispiel für die Definition von Routen, die Handhabung von Authentifizierung oder die Anwendung von Middleware auf bestimmte Ansichten verwendet.
Letzte Worte
Dekoratoren sind unglaublich nützlich. Sie können mit ihnen Funktionen erweitern, ohne deren Funktionalität zu verändern. Dies ist nützlich, wenn Sie die Leistung von Funktionen überwachen, protokollieren möchten, wann eine Funktion aufgerufen wird, Argumente vor dem Aufruf einer Funktion validieren oder Berechtigungen überprüfen möchten, bevor eine Funktion ausgeführt wird. Sobald Sie Dekoratoren verstehen, werden Sie in der Lage sein, Code auf sauberere Weise zu schreiben.
Als nächstes sollten Sie unsere Artikel über Tupel und die Verwendung von cURL in Python lesen.