Menu

Python-Dekoratoren: @-Funktionen, Argumente und functools.wraps

Was Python-Dekoratoren wirklich sind, wie du eigene schreibst und die Muster (Argumente, Stacken, wraps), die sie nutzbar machen.

Ein Dekorator ist eine Funktion, die eine Funktion umhüllt

Der Satz klingt abstrakt, aber die Mechanik ist einfach. Ein Dekorator nimmt eine Funktion entgegen und gibt eine Funktion zurück. Die zurückgegebene Funktion ruft normalerweise die ursprüngliche auf, mit etwas zusätzlichem Verhalten drumherum.

Das kürzeste mögliche Beispiel:

main.py
Output
Click Run to see the output here.

shout ist der Dekorator. Er nimmt eine Funktion (greet), baut eine neue Funktion (wrapper), die das Original aufruft und das Ergebnis in Großbuchstaben wandelt, und gibt sie zurück. Die Neuzuweisung greet = shout(greet) tauscht das Original gegen die umhüllte Version.

Dieses Neuzuweisungsmuster ist so häufig, dass Python ihm eine eigene Syntax gegeben hat.

Das @ ist Zucker für eine Neuzuweisung

@name auf der Zeile über einem def entspricht name = name(...) direkt nach der Funktionsdefinition:

main.py
Output
Click Run to see the output here.

@shout liest sich als „wende den Dekorator shout auf diese Funktion an“. Python führt direkt nach dem def greet = shout(greet) aus — gleiche Mechanik, weniger Tipperei.

Wenn du @name siehst, ersetz es gedanklich durch function = name(function). Mehr bedeutet die Syntax nicht.

Argumente verarbeiten

Die meisten Funktionen nehmen Argumente. Ein brauchbarer Dekorator reicht sie durch. Das Idiom ist *args, **kwargs — Pythons Art, beliebige Argumente zu akzeptieren —, weil der Wrapper sich nicht darum kümmern sollte, was die umhüllte Funktion erwartet:

main.py
Output
Click Run to see the output here.

*args fängt alle positionellen Argumente ab. **kwargs fängt alle Keyword-Argumente ab. Der Wrapper reicht alles unverändert an die umhüllte Funktion weiter und tut dann, wozu der Dekorator da ist — hier: das Ergebnis in Großbuchstaben wandeln.

Das ist die Form, die die meisten echten Dekoratoren annehmen.

Ein nützlicheres Beispiel: Zeitmessung

Gibt aus, wie lange eine Funktion dauert:

main.py
Output
Click Run to see the output here.

Das Muster — vor dem Aufruf etwas tun, nach dem Aufruf etwas tun — ist das, was die meisten Dekoratoren am Ende tun. Logging, Auth-Checks, Retries und Eingabevalidierung folgen alle derselben Form.

Die Originalidentität bewahren: functools.wraps

Eine Funktion zu dekorieren ersetzt sie, das heißt, die umhüllte Funktion verliert ihre ursprünglichen __name__- und __doc__-Attribute:

main.py
Output
Click Run to see the output here.

greet.__name__ ist jetzt "wrapper", und der Docstring ist weg. Das bricht help(), Tracebacks und jedes Werkzeug, das die Funktion inspiziert.

Die Lösung ist eine Zeile: @functools.wraps(func) an der inneren Funktion kopiert die Metadaten hinüber.

main.py
Output
Click Run to see the output here.

Setz immer @wraps(func) an die innere Funktion. Es kostet nichts und vermeidet spätere überraschende Debug-Sitzungen.

Dekoratoren mit Argumenten

Manchmal braucht der Dekorator selbst Konfiguration — „diese Funktion bis zu 3-mal wiederholen“, „mit Level DEBUG loggen“. Das heißt, eine Schicht mehr Verschachtelung: eine äußere Funktion, die die Argumente nimmt und einen Dekorator zurückgibt.

main.py
Output
Click Run to see the output here.

Drei Ebenen klingen nach viel. Lies von außen nach innen:

  1. repeat(times=3) ist ein Funktionsaufruf. Er gibt den decorator zurück.
  2. decorator ist der eigentliche Dekorator — er nimmt eine Funktion und gibt eine umhüllte zurück.
  3. wrapper ist die umhüllte Funktion, die zur Laufzeit läuft.

Diese Form steckt hinter @retry(times=5), @cache(maxsize=100) und Framework-Dekoratoren wie @app.route("/users"). Sobald du das Dreischicht-Muster siehst, lest sich die gesamte Familie gleich.

Dekoratoren stapeln

Du kannst mehr als einen Dekorator auf eine Funktion anwenden. Sie werden von unten nach oben gestapelt — der am nächsten am def läuft zuerst:

main.py
Output
Click Run to see the output here.

add_exclaim wickelt zuerst, fügt das ! hinzu. Dann wickelt shout darum und wandelt alles in Großbuchstaben. Die Ausgabe ist HI, ROSA!.

Die Reihenfolge zählt. Drehst du den Stapel um, bekommst du HI, ROSA! mit dem Ausrufezeichen nach der Großschreibung — visuell hier identisch, aber stell dir einen Dekorator vor, der JSON formatiert: ihn vor oder nach einem Dekorator laufen zu lassen, der Eingaben loggt, kann sehr unterschiedliche Ergebnisse erzeugen.

Eingebaute Dekoratoren, die du siehst

Python und seine Standardbibliothek liefern eine Handvoll Dekoratoren, denen du in echtem Code begegnest:

main.py
Output
Click Run to see the output here.
  • @property macht eine Methode zu einem berechneten Attribut.
  • @staticmethod markiert eine Methode, die weder self noch cls nutzt.
  • @classmethod bekommt die Klasse als cls statt einer Instanz — ideal für alternative Konstruktoren.
  • @functools.lru_cache memoisiert Ergebnisse, sodass wiederholte Aufrufe mit denselben Argumenten in einen Cache treffen.

Framework-Dekoratoren (@app.route, @pytest.fixture, @dataclass) folgen derselben Maschinerie. Nichts Besonderes — nur Funktionen, die Funktionen umhüllen.

Wann du einen schreibst — und wann nicht

Schreib einen Dekorator, wenn du dasselbe Verhalten auf viele Funktionen anwenden willst — Zeiterfassung, Logging, Retries, Autorisierungschecks. Der ganze Witz ist, dass das Verhalten aus dem Funktionsrumpf bleibt.

Lass den Dekorator weg, wenn:

  • Das Verhalten zu einer bestimmten Funktion gehört. Schreib es in die Funktion.
  • Du ihn nur für Tests willst. Ein Fixture oder Parameter ist klarer.
  • Du versucht bist, vier oder fünf zu stapeln. An dem Punkt ist der Kontrollfluss in der Dekorator-Kette versteckt — eine Leserin muss jede Schicht abwickeln, um zu sehen, was tatsächlich läuft. Eine klare Hilfsfunktion liest sich oft besser.

Dekoratoren sind ein scharfes Werkzeug. Gut eingesetzt halten sie Code DRY und die Absicht offensichtlich. Schlecht eingesetzt verbergen sie, was das Programm tut. Bei der Frage „nutzen oder nicht“ lehn dich zu „offensichtlich“.

Als Nächstes: Type Hints

Dekoratoren sind ein häufiger Ort, an dem Type Hints in freier Wildbahn auftauchen — die wrapper-Funktionen annotieren oft ihre Signaturen. Type Hints sind ein kleines Feature, das sich schnell auszahlt, und kommen als Nächstes.

Häufig gestellte Fragen

Was ist ein Dekorator in Python?

Ein Dekorator ist eine Funktion, die eine andere Funktion nimmt und eine neue Funktion zurückgibt — meist eine, die die Originalfunktion mit zusätzlichem Verhalten umhüllt. Du wendest einen Dekorator mit @decorator_name auf der Zeile über einem def an. Die @-Syntax ist die Kurzform für func = decorator_name(func).

Wozu werden Dekoratoren in Python verwendet?

Verhalten rund um eine Funktion hinzuzufügen, ohne ihren Rumpf zu ändern — Logging, Zeiterfassung, Caching, Authentifizierungsprüfungen, Eingabevalidierung, Wiederholungen. Frameworks setzen sie stark ein: @app.route(...) in Flask, @pytest.fixture in pytest, @property und @staticmethod eingebaut.

Kann ich einen eigenen Dekorator schreiben?

Ja. Ein Dekorator ist einfach eine Funktion, die eine Funktion nimmt und eine Funktion zurückgibt. Die meisten eigenen Dekoratoren wickeln den ursprünglichen Aufruf in eine kleine innere Funktion, die vorher, nachher oder drumherum etwas tut. Nutz functools.wraps an der inneren Funktion, um Name und Docstring der Originalfunktion zu erhalten.

Lerne mit Coddy zu programmieren

LOS GEHT'S