Decoratoren in Python entstanden ursprünglich als Mittel, um den Code kompakter und leserlicher zu gestalten, indem sich wiederholendes Verhalten um Funktionen und Methoden herum gekapselt wird. Vor der Einführung der @decorator-Syntax wurden sie explizit angewendet, was das Verständnis des Codes erschwerte. Heute spielen Decoratoren eine Schlüsselrolle bei der Organisation von Logik, wiederholten Prüfungen, Protokollierung, Caching und mehr.
Ein Problem bei der Arbeit mit Decoratoren besteht darin, die Unterschiede zwischen Funktionen, Instanzmethoden und statischen/Klassenmethoden korrekt zu behandeln. Oft treten Fehler auf, die mit dem Verlust von Informationen über die Methode, später/früher Bindung von self und der Funktionssignatur zu tun haben.
Die Lösung besteht darin, bei der Erstellung universeller Decoratoren das Modul functools.wraps zu verwenden, um Metadaten zu speichern, und darauf zu achten, welchen Typ der dekorierte Objekt ist (z.B. richtig zu berücksichtigen, dass Instanzmethoden self als erstes Argument erhalten).
Beispielcode:
import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f"Vor der Funktion: {func.__name__}") result = func(*args, **kwargs) print(f"Nach der Funktion: {func.__name__}") return result return wrapper class Example: @my_decorator def method(self, x): print(f"Methode aufgerufen mit {x}") ex = Example() ex.method(5)
Wichtige Merkmale:
self als erstes Argument erwartet.functools.wraps zu verwenden.Wenn man einen für Funktionen geschriebenen Decorator bei einer Klassenmethode verwendet, ist es dann zwingend erforderlich, functools.wraps zu verwenden, und was passiert mit self?
Ja, der Decorator funktioniert, aber ohne wraps gehen der Funktionsname, die IDE-Hilfe und die Dokumentation verloren. Self ist immer noch das erste Argument, aber der Verlust der Metadaten erschwert das Debugging und die Reflexion.
def bad_decorator(f): def wrapper(*args, **kwargs): print("dekoriert") return f(*args, **kwargs) return wrapper class Test: @bad_decorator def foo(self): pass print(Test().foo.__name__) # wrapper
Kann derselbe Decorator sowohl für eine Instanzmethode als auch für eine statische Methode verwendet werden?
Ja, aber man muss beachten: Statische Methoden erhalten nicht self als erstes Argument. Wenn der Decorator erwartet, mit self zu arbeiten, treten mit @staticmethod/ @classmethod Fehler auf.
Wie beeinflusst der Decorator die Signatur der Methode und die Autovervollständigung?
Ganz einfach: Ohne functools.wraps gehen die Signatur und der Docstring verloren, die IDE und viele Vorschlagswerkzeuge funktionieren nicht mehr richtig.
Negativer Fall: Decoratoren ohne functools.wraps in allen Methoden einer Klasse.
Vorteile: schnelles Prototyping, funktioniert.
Nachteile: Fehler können nicht über den Stack gefunden werden, IDE schlägt die Signatur nicht vor.
Positiver Fall: Decoratoren verwenden functools.wraps, der Code ist dokumentiert.
Vorteile: Lesbarkeit, Wartung, IDE-Komfort.
Nachteile: minimale zusätzliche Syntax und Aufmerksamkeit.