ProgrammierungPython Backend Entwickler

Was sind Dekoratoren für Funktionen in Python, wie ist ihre Geschichte und wozu werden sie in der modernen Programmierung verwendet?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort

Geschichte der Frage

Dekoratoren wurden in Python mit Version 2.4 als syntaktischer Zucker eingeführt, um die Arbeit mit Funktionen höherer Ordnung zu erleichtern – also solchen, die andere Funktionen akzeptieren oder zurückgeben. Die Evolution der Ansätze zur Erweiterung der Funktionalität von Funktionen führte zu einem Format prägnanter und ausdrucksvoller Mittel – Annotationen durch @decorator.

Problem

In großen Projekten ist es oft erforderlich, Funktionen mit einer zusätzlichen Logik zu modifizieren oder zu umhüllen: Protokollierung, Zugriffskontrolle, Caching, Zeitmessung. Ohne Dekoratoren musste man umhüllende Funktionen manuell aufrufen, was den Code aufblähte.

Lösung

Dekoratoren ermöglichen es, wiederkehrende Aspekte in separate Wrapper auszulagern, wodurch die Lesbarkeit und Wiederverwendbarkeit des Codes erhöht wird. Mit @decorator kann man elegant Funktionalität zu Funktionen und Methoden hinzufügen:

Beispielcode:

import time def timing_decorator(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print(f'Elapsed: {time.time() - start:.3f}s') return result return wrapper @timing_decorator def slow_function(): time.sleep(0.5) slow_function() # Gibt aus, wie lange die Ausführung gedauert hat

Wesentliche Merkmale:

  • Erlauben die Wiederverwendung von Code (DRY-Prinzip);
  • Können für Funktionen, Methoden und Klassen verwendet werden;
  • Erlauben die zentralisierte Implementierung von bereichsübergreifenden Aufgaben (Protokollierung, Caching, Zugriff, Profiling);

Fragen mit einem Twist.

Können Dekoratoren die Signatur der umhüllenden Funktion ändern?

Oft wird fälschlicherweise angenommen, dass die Signatur der Funktion durch den Dekorator nicht geändert wird. Tatsächlich verschwindet ohne die Verwendung des Moduls functools.wraps die Metainformation, was unerwartete Fehler in der Autodokumentation oder bei der Introspektion verursachen kann.

Beispielcode:

from functools import wraps def decorator(func): @wraps(func) def wrapper(*args): return func(*args) return wrapper

Können Dekoratoren parametrisiert werden?

Oft wird geantwortet, dass dies nicht möglich ist. Tatsächlich kann man einen parametrisierten Dekorator durch eine zusätzliche Ebene der Verschachtelung – eine Funktion, die einen Dekorator zurückgibt – erstellen.

Beispielcode:

def repeat(n): def decorator(func): def wrapper(*args, **kwargs): result = None for _ in range(n): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(3) def hello(): print("Hello!")

Kann man mehrere Dekoratoren für eine Funktion anwenden? In welcher Reihenfolge wird sie ausgeführt?

Oft wird fälschlicherweise angenommen, dass die Reihenfolge unwichtig ist oder der Reihenfolge der Dekoratoren im Code entspricht. Tatsächlich wird zuerst der unterste Dekorator angewendet, dann der nächste und so weiter nach oben.

Beispielcode:

def dec1(f): def wrapper(*a, **k): print("dec1") return f(*a, **k) return wrapper def dec2(f): def wrapper(*a, **k): print("dec2") return f(*a, **k) return wrapper @dec1 @dec2 def f(): print("core") f() # dec1, dec2, core

Typische Fehler und Anti-Pattern

  • Ignorieren von functools.wraps verliert Metainformationen über die Originalfunktion;
  • Die Logik des Dekorators berücksichtigt keine Ausnahmen, Fehler können nicht innerhalb gefangen und verarbeitet werden;
  • Unklare Überlagerung mehrerer Dekoratoren

Beispiel aus dem Leben

Negativer Fall

Die Protokollierung der Laufzeit wurde manuell für 10 Funktionen hinzugefügt, durch Kopieren und Einfügen.

Vorteile:

  • Logik ist klar, der Code ist nah, Fehler leicht zu finden.

Nachteile:

  • Schwer zu warten – es müssen Dutzende von Stellen geändert werden, wenn das Verhalten geändert werden muss.
  • Code wird dupliziert, verletzt DRY.

Positiver Fall

Die gesamte Logik der Zeitmessung wurde in einen Dekorator ausgelagert, alle Funktionen, bei denen diese Metrik erforderlich ist, wurden mit dem Dekorator @timing_decorator umhüllt.

Vorteile:

  • Änderungen erfolgen zentral;
  • Der Code ist kürzer und leserlicher.

Nachteile:

  • Möglicher Verlust von Informationen über die Signatur ohne functools.wraps, wenn man nicht vorsichtig ist;
  • Für Neulinge kann es schwieriger sein, die Mechanik der Dekoratoren auf Anhieb zu verstehen.