Direkt zum Inhalt

Python Dekoratoren Tutorial

In diesem Lernprogramm lernst du, wie du Dekoratoren in Python implementierst.
Aktualisierte 4. Dez. 2024  · 11 Min. Lesezeit

Ein Dekorator ist ein Entwurfsmuster in Python, mit dem man 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 gesamten Beispielcode in diesem Lehrgang ganz einfach selbst auszuführen, kannst du dir kostenlos eine 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

Beherrsche Python für Data Science und erwerbe gefragte Fähigkeiten.
Kostenloses Lernen Beginnen

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

Definieren von Funktionen innerhalb anderer Funktionen 

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 das Erstellen und Verstehen 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'

Verschlüsse verstehen

In Python kann eine verschachtelte Funktion auf den äußeren Bereich der umschließenden Funktion zugreifen. Dies ist ein entscheidendes Konzept in Dekoratoren, das als Closure bezeichnet wird.

Eine Closure in Python ist eine Funktion, die sich die Umgebung, in der sie erstellt wurde, merkt, auch wenn diese Umgebung nicht mehr aktiv ist. Das bedeutet, dass eine verschachtelte Funktion Variablen aus dem umschließenden Bereich "überschreiben" kann und sie weiterhin verwendet.

Closures sind für das Verständnis von Dekoratoren unerlässlich, weil Dekoratoren auf die Fähigkeit einer verschachtelten Wrapper-Funktion angewiesen sind, auf den Zustand der umschließenden Dekorator-Funktion zuzugreifen und diesen zu verändern.

Beispiel für einen Abschluss:

def outer_function(message):
    def inner_function():
        print(f"Message from closure: {message}")
    return inner_function

closure_function = outer_function("Hello, closures!")
closure_function()
# Output: Message from closure: Hello, closures!

In diesem Beispiel:

  • inner_function ist eine Schließung, weil sie auf message zugreift, eine Variable aus dem umschließenden Bereich (outer_function).
  • Auch wenn outer_function die Ausführung beendet hat, behält inner_function den Zugriff auf message.

Wenn du einen Dekorator erstellst, ist die Wrapper-Funktion (innerhalb des Dekorators) eine Closure. Sie behält den Zugriff auf die dekorierte Funktion und alle zusätzlichen Zustände oder Argumente, die in der Dekoratorfunktion definiert sind. Zum Beispiel:

def simple_decorator(func):
    def wrapper():
        print("Before the function call")
        func()
        print("After the function call")
    return wrapper

@simple_decorator
def greet():
    print("Hello!")

greet()
# Output:
# Before the function call
# Hello!
# After the function call

Hier ist wrapper eine Schließung, die sich die Funktion greet merkt und Verhalten vor und nach ihrer Ausführung hinzufügt.

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 Dekorfunktion nimmt eine Funktion als Argument an. Wir werden also eine Funktion definieren und sie an unseren Dekorfaktor ü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 dabei, Klarheit und Konsistenz bei der Fehlersuche und dem Verständnis der Eigenschaften der dekorierten Funktion zu erhalten.

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 dem Dekorator selbst Argumente übergeben können. 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.

Klassenbasierte Dekorateure

Während funktionsbasierte Dekoratoren weit verbreitet sind, kannst du mit Python auch klassenbasierte Dekoratoren erstellen, die vor allem bei komplexen Anwendungsfällen mehr Flexibilität und Wartungsfreundlichkeit bieten. Ein klassenbasierter Dekorator ist eine Klasse mit einer __call__ Methode, die es ihr ermöglicht, sich wie eine Funktion zu verhalten.

class UppercaseDecorator:
    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        result = self.function(*args, **kwargs)
        return result.upper()

@UppercaseDecorator
def greet():
    return "hello there"

print(greet())
# Output: HELLO THERE

So funktioniert es:

  1. Die Methode __init__ initialisiert den Dekorator mit der Funktion, die dekoriert werden soll.
  2. Die Methode __call__ wird aufgerufen, wenn die dekorierte Funktion aufgerufen wird, damit der Dekorator ihr Verhalten ändern kann.

Vorteile von klassenbasierten Dekoratoren:

  • Zustandsbezogene Dekoratoren: Klassenbasierte Dekoratoren können ihren Zustand mithilfe von Instanzvariablen aufrechterhalten, im Gegensatz zu funktionsbasierten Dekoratoren, die Closures oder globale Variablen benötigen.
  • Lesbarkeit: Bei komplexen Dekoratoren kann die Kapselung der Logik in einer Klasse den Code übersichtlicher und leichter verständlich machen.

Beispiel für einen zustandsabhängigen Dekorator:

class CallCounter:
    def __init__(self, function):
        self.function = function
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Function {self.function.__name__} has been called {self.count} times.")
        return self.function(*args, **kwargs)

@CallCounter
def say_hello():
    print("Hello!")

say_hello()
say_hello()
# Output:
# Function say_hello has been called 1 times.
# Hello!
# Function say_hello has been called 2 times.
# Hello!

Real-World Decorator Use Case: Caching

Der lru_cache Dekorator ist ein eingebautes Werkzeug in Python, das die Ergebnisse teurer Funktionsaufrufe zwischenspeichert. Dies verbessert die Leistung, da redundante Berechnungen für wiederholte Eingaben vermieden werden.

Beispiel:

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(50))  # Subsequent calls with the same argument are much faster

Andere häufige Verwendungen für Dekorateure:

  • Loggen: Verfolge Funktionsaufrufe, Argumente und Rückgabewerte zum Debuggen oder Prüfen.

  • Authentifizierung: Durchsetzung der Zugriffskontrolle in Webanwendungen wie Flask oder Django.

  • Ausführungszeitpunkt: Messe und optimiere die Ausführungszeit von Funktionen für leistungskritische Aufgaben.

  • Wiederholungsmechanismus: Versucht automatisch, fehlgeschlagene Funktionsaufrufe zu wiederholen, was bei Netzwerkoperationen nützlich ist.

  • Eingabeüberprüfung: Validiere die Funktionsargumente vor der Ausführung.

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.

Werde ein Python-Entwickler

Erwerbe die Programmierkenntnisse, die alle Python-Entwickler/innen brauchen.

FAQs

Gibt es bei der Verwendung von Dekoratoren irgendwelche Leistungsüberlegungen?

Ja, Dekoratoren können zusätzlichen Aufwand verursachen, weil sie zusätzliche Funktionsaufrufe einführen. Wenn die Leistung entscheidend ist, ist es wichtig, diesen Overhead zu berücksichtigen, vor allem wenn die dekorierte Funktion häufig in einem leistungssensiblen Kontext aufgerufen wird.

Können Dekoratoren mit Klassenmethoden verwendet werden, und wenn ja, wie?

Ja, Dekoratoren können auf Klassenmethoden genauso wie auf normale Funktionen angewendet werden. Der Dekorator erhält die Methode als Argument und gibt eine neue Methode oder eine modifizierte Version der Methode zurück. Dies wird häufig für die Protokollierung, die Zugriffskontrolle oder die Durchsetzung von Vorbedingungen verwendet.

Wie können Dekoratoren für die Protokollierung verwendet werden?

Decorators können verwendet werden, um Funktionsaufrufe, ihre Argumente und Rückgabewerte zu protokollieren, indem die Funktionsausführung mit Code verpackt wird, der diese Details in einem Logging-System aufzeichnet. Das hilft bei der Nachverfolgung und Fehlersuche.

Welche Bedeutung hat das @-Symbol in Dekoratoren?

Das Symbol@ ist ein syntaktischer Zucker in Python, der die Anwendung eines Dekorators auf eine Funktion vereinfacht. Damit kannst du einen Dekorator auf eine Funktion direkt über ihrer Definition anwenden, wodurch der Code sauberer und lesbarer wird.

Kann ein Dekorator den Rückgabewert einer Funktion ändern, und wie würde das funktionieren?

Ja, ein Dekorator kann den Rückgabewert einer Funktion ändern, indem er die Rückgabeanweisung innerhalb der Wrapper-Funktion ändert. Zum Beispiel könnte sie den Ausgabedatentyp umwandeln, ihn formatieren oder eine zusätzliche Verarbeitung vornehmen, bevor sie das Endergebnis zurückgibt.

Wie behandelt Python den Geltungsbereich von Variablen, wenn eine verschachtelte Funktion auf eine Variable aus ihrer umschließenden Funktion zugreift?

Python verwendet eine LEGB (Local, Enclosing, Global, Built-in) Bereichsregel. Bei verschachtelten Funktionen kann die verschachtelte Funktion auf Variablen aus dem Bereich der umschließenden Funktion zugreifen, was Schließungen ermöglicht, bei denen die innere Funktion den Zugriff auf die Variablen der äußeren Funktion behält, auch nachdem die äußere Funktion ihre Ausführung beendet hat.

Themen

Erfahre mehr über Python

Zertifizierung verfügbar

Kurs

Funktionen in Python schreiben

4 hr
91.3K
Lerne, Best Practices anzuwenden, um wartbare, wiederverwendbare, komplexe Funktionen mit guter Dokumentation zu schreiben.
Siehe DetailsRight Arrow
Kurs Starten
Mehr anzeigenRight Arrow
Verwandt

Der Blog

Die 20 besten Snowflake-Interview-Fragen für alle Niveaus

Bist du gerade auf der Suche nach einem Job, der Snowflake nutzt? Bereite dich mit diesen 20 besten Snowflake-Interview-Fragen vor, damit du den Job bekommst!
Nisha Arya Ahmed's photo

Nisha Arya Ahmed

20 Min.

Der Blog

Top 30 Generative KI Interview Fragen und Antworten für 2024

Dieser Blog bietet eine umfassende Sammlung von Fragen und Antworten zu generativen KI-Interviews, die von grundlegenden Konzepten bis hin zu fortgeschrittenen Themen reichen.
Hesam Sheikh Hassani's photo

Hesam Sheikh Hassani

15 Min.

Der Blog

Die 32 besten AWS-Interview-Fragen und Antworten für 2024

Ein kompletter Leitfaden zur Erkundung der grundlegenden, mittleren und fortgeschrittenen AWS-Interview-Fragen, zusammen mit Fragen, die auf realen Situationen basieren. Es deckt alle Bereiche ab und sorgt so für eine abgerundete Vorbereitungsstrategie.
Zoumana Keita 's photo

Zoumana Keita

30 Min.

See MoreSee More