Course
Python Dekoratoren Tutorial
Ein Dekorator ist ein Entwurfsmuster in Python, mit dem ein Benutzer einem bestehenden Objekt neue Funktionen hinzufügen kann, ohne seine Struktur zu verändern. Dekoratoren werden in der Regel auf Funktionen angewendet und spielen eine entscheidende Rolle bei der Verbesserung oder Änderung des Verhaltens von Funktionen. Traditionell werden Dekoratoren vor der Definition einer Funktion platziert, die du dekorieren möchtest. In diesem Lernprogramm zeigen wir dir, wie du Dekoratoren in Python-Funktionen effektiv einsetzen kannst.
Funktionen in Python sind Bürger erster Klasse. Das bedeutet, dass sie Operationen wie die Übergabe als Argument, die Rückgabe aus einer Funktion, die Änderung und die Zuweisung an eine Variable unterstützen. Diese Eigenschaft ist wichtig, da sie es ermöglicht, Funktionen wie jedes andere Objekt in Python zu behandeln, was eine größere Flexibilität bei der Programmierung ermöglicht.
Um den Beispielcode in diesem Lernprogramm ganz einfach selbst auszuführen, kannst du eine kostenlose DataLab-Arbeitsmappe erstellen, auf der Python vorinstalliert ist und die alle Codebeispiele enthält. Wenn du mehr über Dekoratoren erfahren möchtest, schau dir diese praktische DataCamp-Übung an.
Python von Grund auf lernen
Zuweisung von Funktionen zu Variablen
Zu Beginn erstellen wir eine Funktion, die bei jedem Aufruf eine Eins zu einer Zahl addiert. Dann weisen wir die Funktion einer Variablen zu und verwenden diese Variable, um die Funktion aufzurufen.
def plus_one(number):
return number + 1
add_one = plus_one
add_one(5)
6
Funktionen innerhalb anderer Funktionen definieren
Als Nächstes zeigen wir dir, wie du eine Funktion innerhalb einer anderen Funktion in Python definieren kannst. Bleib dran, wir werden bald herausfinden, wie all das für die Erstellung und das Verständnis von Dekoratoren in Python relevant ist.
def plus_one(number):
def add_one(number):
return number + 1
result = add_one(number)
return result
plus_one(4)
5
Übergabe von Funktionen als Argumente an andere Funktionen
Funktionen können auch als Parameter an andere Funktionen übergeben werden. Das wollen wir im Folgenden veranschaulichen.
def plus_one(number):
return number + 1
def function_call(function):
number_to_add = 5
return function(number_to_add)
function_call(plus_one)
6
Funktionen, die andere Funktionen zurückgeben
Eine Funktion kann auch eine andere Funktion erzeugen. Das zeigen wir weiter unten anhand eines Beispiels.
def hello_function():
def say_hi():
return "Hi"
return say_hi
hello = hello_function()
hello()
'Hi'
Verschachtelte Funktionen haben Zugriff auf den Variablenbereich der einschließenden Funktion
Python erlaubt es einer verschachtelten Funktion, auf den äußeren Bereich der umschließenden Funktion zuzugreifen. Dies ist ein wichtiges Konzept bei Dekoratoren - dieses Muster wird als Closure bezeichnet.
def print_message(message):
"Enclosong Function"
def message_sender():
"Nested Function"
print(message)
message_sender()
print_message("Some random message")
Some random message
Dekorateure schaffen
Nachdem diese Voraussetzungen geklärt sind, wollen wir einen einfachen Dekorator erstellen, der einen Satz in Großbuchstaben umwandelt. Wir tun dies, indem wir einen Wrapper innerhalb einer eingeschlossenen Funktion definieren. Wie du siehst, ist sie der Funktion innerhalb einer anderen Funktion, die wir zuvor erstellt haben, sehr ähnlich.
def uppercase_decorator(function):
def wrapper():
func = function()
make_uppercase = func.upper()
return make_uppercase
return wrapper
Unsere Dekoratorfunktion nimmt eine Funktion als Argument an. Wir werden also eine Funktion definieren und sie an unseren Dekorator übergeben. Wir haben bereits gelernt, dass wir eine Funktion einer Variablen zuweisen können. Wir verwenden diesen Trick, um unsere Dekoratorfunktion aufzurufen.
def say_hi():
return 'hello there'
decorate = uppercase_decorator(say_hi)
decorate()
'HELLO THERE'
Python bietet uns jedoch eine viel einfachere Möglichkeit, Dekoratoren anzuwenden. Wir verwenden einfach das @-Symbol vor der Funktion, die wir ausschmücken möchten. Das wollen wir im Folgenden in der Praxis zeigen.
@uppercase_decorator
def say_hi():
return 'hello there'
say_hi()
'HELLO THERE'
Mehrere Dekoratoren auf eine einzige Funktion anwenden
Wir können mehrere Dekoratoren für eine einzige Funktion verwenden. Die Dekoratoren werden jedoch in der Reihenfolge angewendet, in der wir sie aufgerufen haben. Im Folgenden werden wir einen weiteren Dekorator definieren, der den Satz in eine Liste aufteilt. Dann wenden wir den uppercase_decorator
und split_string
Dekorator auf eine einzelne Funktion an.
import functools
def split_string(function):
@functools.wraps(function)
def wrapper():
func = function()
splitted_string = func.split()
return splitted_string
return wrapper
@split_string
@uppercase_decorator
def say_hi():
return 'hello there'
say_hi()
['HELLO', 'THERE']
Anhand der obigen Ausgabe sehen wir, dass die Anwendung der Dekoratoren von unten nach oben erfolgt. Hätten wir die Reihenfolge vertauscht, hätten wir einen Fehler gesehen, da Listen kein upper
Attribut haben. Der Satz wurde zuerst in Großbuchstaben umgewandelt und dann in eine Liste aufgeteilt.
Hinweis: Beim Stapeln von Dekoratoren ist es gängige Praxis, functools.wraps
zu verwenden, um sicherzustellen, dass die Metadaten der ursprünglichen Funktion während des Stapelvorgangs erhalten bleiben. Das hilft bei der Fehlersuche und dem Verständnis der Eigenschaften der dekorierten Funktion, Klarheit und Konsistenz zu bewahren.
Akzeptieren von Argumenten in Decorator-Funktionen
Manchmal müssen wir vielleicht einen Dekorator definieren, der Argumente akzeptiert. Wir erreichen dies, indem wir die Argumente an die Wrapper-Funktion übergeben. Die Argumente werden dann an die Funktion übergeben, die zum Zeitpunkt des Aufrufs dekoriert wird.
def decorator_with_arguments(function):
def wrapper_accepting_arguments(arg1, arg2):
print("My arguments are: {0}, {1}".format(arg1,arg2))
function(arg1, arg2)
return wrapper_accepting_arguments
@decorator_with_arguments
def cities(city_one, city_two):
print("Cities I love are {0} and {1}".format(city_one, city_two))
cities("Nairobi", "Accra")
My arguments are: Nairobi, Accra
Cities I love are Nairobi and Accra
Hinweis: Es ist wichtig sicherzustellen, dass die Anzahl der Argumente im Dekorator (arg1, arg2
in diesem Beispiel) mit der Anzahl der Argumente in der umschlossenen Funktion (cities
in diesem Beispiel) übereinstimmt. Diese Ausrichtung ist wichtig, um Fehler zu vermeiden und die korrekte Funktionalität bei der Verwendung von Dekoratoren mit Argumenten sicherzustellen.
Definition von Allzweckdekoratoren
Um einen Allzweckdekorator zu definieren, der auf jede Funktion angewendet werden kann, verwenden wir args
und **kwargs
. args
und **kwargs
sammeln alle Positions- und Schlüsselwortargumente und speichern sie in den Variablen args und kwargs. args
und kwargs
ermöglichen es uns, bei Funktionsaufrufen so viele Argumente zu übergeben, wie wir möchten.
def a_decorator_passing_arbitrary_arguments(function_to_decorate):
def a_wrapper_accepting_arbitrary_arguments(*args,**kwargs):
print('The positional arguments are', args)
print('The keyword arguments are', kwargs)
function_to_decorate(*args)
return a_wrapper_accepting_arbitrary_arguments
@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
print("No arguments here.")
function_with_no_argument()
The positional arguments are ()
The keyword arguments are {}
No arguments here.
Schauen wir uns an, wie wir den Dekorator mit Positionsargumenten verwenden können.
@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
print(a, b, c)
function_with_arguments(1,2,3)
The positional arguments are (1, 2, 3)
The keyword arguments are {}
1 2 3
Schlüsselwortargumente werden mit Schlüsselwörtern übergeben. Eine Illustration dazu findest du unten.
@a_decorator_passing_arbitrary_arguments
def function_with_keyword_arguments():
print("This has shown keyword arguments")
function_with_keyword_arguments(first_name="Derrick", last_name="Mwiti")
The positional arguments are ()
The keyword arguments are {'first_name': 'Derrick', 'last_name': 'Mwiti'}
This has shown keyword arguments
Hinweis: Die Verwendung von **kwargs
im Dekorator ermöglicht es, Schlüsselwortargumente zu behandeln. Dadurch ist der Allzweckdekorator vielseitig und kann eine Vielzahl von Argumenttypen bei Funktionsaufrufen verarbeiten.
Übergabe von Argumenten an den Decorator
Schauen wir uns nun an, wie wir Argumente an den Dekorator selbst übergeben. Um dies zu erreichen, definieren wir einen Dekorator-Maker, der Argumente akzeptiert, und definieren dann einen Dekorator in ihm. Dann definieren wir eine Wrapper-Funktion innerhalb des Dekorators, so wie wir es zuvor getan haben.
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2, decorator_arg3):
def decorator(func):
def wrapper(function_arg1, function_arg2, function_arg3) :
"This is the wrapper function"
print("The wrapper can access all the variables\n"
"\t- from the decorator maker: {0} {1} {2}\n"
"\t- from the function call: {3} {4} {5}\n"
"and pass them to the decorated function"
.format(decorator_arg1, decorator_arg2,decorator_arg3,
function_arg1, function_arg2,function_arg3))
return func(function_arg1, function_arg2,function_arg3)
return wrapper
return decorator
pandas = "Pandas"
@decorator_maker_with_arguments(pandas, "Numpy","Scikit-learn")
def decorated_function_with_arguments(function_arg1, function_arg2,function_arg3):
print("This is the decorated function and it only knows about its arguments: {0}"
" {1}" " {2}".format(function_arg1, function_arg2,function_arg3))
decorated_function_with_arguments(pandas, "Science", "Tools")
The wrapper can access all the variables
- from the decorator maker: Pandas Numpy Scikit-learn
- from the function call: Pandas Science Tools
and pass them to the decorated function
This is the decorated function, and it only knows about its arguments: Pandas Science Tools
Debuggen von Dekoratoren
Wie wir gesehen haben, umhüllen Dekoratoren Funktionen. Der ursprüngliche Funktionsname, der Docstring und die Parameterliste werden von der Wrapper-Schließung verborgen: Wenn wir zum Beispiel versuchen, auf die Metadaten von decorated_function_with_arguments
zuzugreifen, sehen wir die Metadaten der Wrapper-Schließung. Dies stellt eine Herausforderung bei der Fehlersuche dar.
decorated_function_with_arguments.__name__
'wrapper'
decorated_function_with_arguments.__doc__
'This is the wrapper function'
Um diese Herausforderung zu lösen, bietet Python einen functools.wraps
Dekorator. Dieser Dekorator kopiert die verlorenen Metadaten von der undekorierten Funktion in die dekorierte Schließung. Lass uns zeigen, wie wir das machen würden.
import functools
def uppercase_decorator(func):
@functools.wraps(func)
def wrapper():
return func().upper()
return wrapper
@uppercase_decorator
def say_hi():
"This will say hi"
return 'hello there'
say_hi()
'HELLO THERE'
Wenn wir die Metadaten von say_hi
überprüfen, stellen wir fest, dass sie sich jetzt auf die Metadaten der Funktion beziehen und nicht auf die Metadaten des Wrappers.
say_hi.__name__
'say_hi'
say_hi.__doc__
'This will say hi'
Es ist ratsam und gute Praxis, bei der Definition von Dekoratoren immer functools.wraps
zu verwenden. Das erspart dir eine Menge Kopfschmerzen bei der Fehlersuche.
Python-Dekoratoren Zusammenfassung
Decorators verändern dynamisch die Funktionalität einer Funktion, Methode oder Klasse, ohne direkt Unterklassen zu verwenden oder den Quellcode der zu dekorierenden Funktion zu ändern. Die Verwendung von Dekoratoren in Python stellt außerdem sicher, dass dein Code DRY (Don't Repeat Yourself) ist. Dekoratoren haben mehrere Anwendungsfälle, wie zum Beispiel:
- Autorisierung in Python-Frameworks wie Flask und Django
- Loggen
- Messung der Ausführungszeit
- Synchronisation
Mehr über Python-Dekoratoren erfährst du in der Python-Dekorator-Bibliothek.
Erfahre mehr über Python
Course
Writing Efficient Python Code
Course
Introduction to Functions in Python
Der Blog