Python-Dekorierer-Muster
Die Kernidee des Dekorator-Musters ist das Umhüllen — indem ein Wrapper-Objekt (Dekorator) erstellt wird, das das ursprüngliche Objekt umhüllt, wodurch seine Funktionalität erweitert wird, ohne das ursprüngliche Objekt zu verändern.
Grundlegende Konzepte
Das Dekorator-Muster besteht aus vier Hauptrollen:
- Komponenten-Schnittstelle (Component): Definiert die gemeinsame Schnittstelle für das dekorierte Objekt und den Dekorator
- Konkrete Komponente: Das ursprüngliche Objekt, das dekoriert werden muss
- Dekorator-Basisklasse (Decorator): Hält eine Referenz auf ein Komponentenobjekt und implementiert die Komponenten-Schnittstelle
- Konkreter Dekorator: Implementiert die spezifische Dekorationsfunktionalität

Grundlegende Syntax von Dekoratoren
Funktionsdekoratoren
Funktionsdekoratoren sind die gebräuchlichste Form von Dekoratoren. Sie nehmen eine Funktion als Argument entgegen und geben eine neue Funktion zurück.
Beispiel
def wrapper(*args, **kwargs):
print("Einige Operationen vor der Funktionsausführung")
result = func(*args, **kwargs)
print("Einige Operationen nach der Funktionsausführung")
return result
return wrapper
@my_decorator
def say_hello(name):
print(f"Hallo, {name}!")
# Dekorator verwenden
say_hello("Alice")
Ausgabe:
函数执行前的一些操作 Hello, Alice! 函数执行后的一些操作
Dekoratoren mit Parametern
Wenn Sie Parameter an den Dekorator übergeben müssen, müssen Sie eine weitere Ebene von Funktionen verschachteln:
Beispiel
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(times):
print(f"Ausführung {i+1}:")
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hallo, {name}!")
greet("Bob")
Klassendekoratoren
Neben Funktionsdekoratoren unterstützt Python auch Klassendekoratoren. Klassendekoratoren funktionieren, indem sie__call__Methoden implementieren, um zu funktionieren.
Einfacher Klassendekorator
Beispiel
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
import time
start_time = time.time()
result = self.func(*args, **kwargs)
end_time = time.time()
print(f"Funktion {self.func.__name__} ausgeführt in {end_time - start_time:.4f} Sekunden")
return result
@TimerDecorator
def calculate_sum(n):
return sum(range(n))
result = calculate_sum(1000000)
print(f"Berechnungsergebnis: {result}")
Klassen-Dekorator mit Parametern
Beispiel
def __init__(self, level="INFO"):
self.level = level
def __call__(self, func):
def wrapper(*args, **kwargs):
print(f"[{self.level}] Funktion wird aufgerufen: {func.__name__}")
print(f"[{self.level}] Argumente: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"[{self.level}] Rückgabewert: {result}")
return result
return wrapper
@LogDecorator(level="DEBUG")
def multiply(a, b):
return a * b
multiply(5, 3)
Eingebaute Dekoratoren
Python bietet einige nützliche eingebaute Dekoratoren:
@staticmethod und @classmethod
Beispiel
@staticmethod
def add(x, y):
return x + y
@classmethod
def multiply(cls, x, y):
return x * y
# Statische Methode verwenden
result1 = Calculator.add(5, 3)
print(f"Ergebnis der statischen Methode: {result1}")
# Klassenmethode verwenden
result2 = Calculator.multiply(5, 3)
print(f"Ergebnis der Klassenmethode: {result2}")
@property
Beispiel
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("Radius muss eine positive Zahl sein")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2
circle = Circle(5)
print(f"Radius: {circle.radius}")
print(f"Fläche: {circle.area}")
circle.radius = 10
print(f"Neuer Radius: {circle.radius}")
print(f"Neue Fläche: {circle.area}")
Praktische Anwendungsszenarien von Dekoratoren
1. Protokollierung
Beispiel
def wrapper(*args, **kwargs):
print(f"Starte Ausführung: {func.__name__}")
try:
result = func(*args, **kwargs)
print(f"Erfolgreich abgeschlossen: {func.__name__}")
return result
except Exception as e:
print(f"Ausführung fehlgeschlagen: {func.__name__}, Fehler: {e}")
raise
return wrapper
@log_execution
def process_data(data):
# Datenverarbeitung simulieren
if not data:
raise ValueError("Daten dürfen nicht leer sein")
return [x * 2 for x in data]
# Test
data = [1, 2, 3]
result = process_data(data)
print(f"Verarbeitungsergebnis: {result}")
2. Berechtigungsüberprüfung
Beispiel
def wrapper(user, *args, **kwargs):
if not user.get('is_authenticated', False):
raise PermissionError("Benutzer ist nicht angemeldet")
return func(user, *args, **kwargs)
return wrapper
@require_login
def view_profile(user):
return f"Profil von Benutzer {user['username']} anzeigen"
# Test
user1 = {'username': 'alice', 'is_authenticated': True}
user2 = {'username': 'bob', 'is_authenticated': False}
print(view_profile(user1)) # Normale Ausführung
# print(view_profile(user2)) # wird einen PermissionError auslösen
3. Cache-Dekorator
Beispiel
cache = {}
def wrapper(*args):
if args in cache:
print(f"Ergebnis aus dem Cache holen: {args}")
return cache[args]
result = func(*args)
cache[args] = result
print(f"Ergebnis berechnen und zwischenspeichern: {args} -> {result}")
return result
return wrapper
@cache_results
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# Cache-Effekt testen
print(fibonacci(5))
print(fibonacci(5)) # Dieses Mal wird es aus dem Cache abgerufen
Ausführungsreihenfolge mehrerer Dekoratoren
Bei Verwendung mehrerer Dekoratoren verläuft ihre Ausführungsreihenfolge von unten nach oben:
Beispiel
def wrapper():
print("Dekorator 1 - davor")
func()
print("Dekorator 1 - danach")
return wrapper
def decorator2(func):
def wrapper():
print("Dekorator 2 - davor")
func()
print("Dekorator 2 - danach")
return wrapper
@decorator1
@decorator2
def my_function():
print("Ursprüngliche Funktion")
my_function()
Ausgabe:
装饰器1 - 前 装饰器2 - 前 原始函数 装饰器2 - 后 装饰器1 - 后
Beibehalten der Funktionsmetadaten
Wenn Sie Dekoratoren verwenden, werden die Metadaten der ursprünglichen Funktion (wie Funktionsname, Docstring usw.) durch die Wrapper-Funktion überschrieben. Sie könnenfunctools.wrapsum diese Informationen beizubehalten:
Beispiel
def preserve_metadata(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""Docstring der Wrapper-Funktion"""
return func(*args, **kwargs)
return wrapper
@preserve_metadata
def example_function():
"""Dies ist der Docstring der ursprünglichen Funktion"""
pass
print(f"Funktionsname: {example_function.__name__}")
print(f"Dokumentationsstring: {example_function.__doc__}")
Praktische Übungen
Übung 1: Erstellen eines Dekorators zur Leistungsüberwachung
Schreibe einen Dekorator, um die Ausführungszeit und den Speicherverbrauch einer Funktion zu überwachen.
Beispiel
import tracemalloc
def performance_monitor(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Speicherüberwachung starten
tracemalloc.start()
# Startzeit erfassen
start_time = time.time()
# Funktion ausführen
result = func(*args, **kwargs)
# Endzeit erfassen
end_time = time.time()
# Speicherverbrauch abrufen
current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f"Leistungsbericht für Funktion {func.__name__}:")
print(f"Ausführungszeit: {end_time - start_time:.4f} Sekunden")
print(f"Speicherverbrauch: aktuell {current/1024:.2f} KB, Spitzenwert {peak/1024:.2f} KB")
return result
return wrapper
@performance_monitor
def process_large_data():
"""Simuliert die Verarbeitung großer Datenmengen"""
data = [i**2 for i in range(100000)]
return sum(data)
process_large_data()
Übung 2: Implementierung eines Dekorators für Wiederholungsmechanismen
Erstelle einen Dekorator, der bei fehlgeschlagener Funktionsausführung automatisch eine angegebene Anzahl von Wiederholungen durchführt.
Beispiel
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
if attempts == max_attempts:
print(f"Funktion {func.__name__} fehlgeschlagen, maximale Anzahl an Wiederholungsversuchen erreicht")
raise
print(f"Funktion {func.__name__} fehlgeschlagen bei Versuch {attempts}: {e}")
print(f"Warte {delay} Sekunden vor dem erneuten Versuch...")
time.sleep(delay)
return None
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def unstable_operation():
"""Simuliert eine instabile Operation"""
import random
if random.random() < 0.7: # 70% Wahrscheinlichkeit eines Fehlers
raise ValueError("Zufälliger Fehler")
return "Operation erfolgreich"
# Wiederholungsmechanismus testen
result = unstable_operation()
print(f"Endergebnis: {result}")
Zusammenfassung
Python-Dekoratoren sind ein mächtiges und flexibles Werkzeug, das die folgenden Vorteile bietet:
- Code-Wiederverwendung: Trennt querschnittliche Belange (wie Logging, Caching, Validierung) von der Geschäftslogik
- Dynamische Erweiterung: Neue Funktionalität hinzufügen, ohne den ursprünglichen Code zu ändern
- Prägnant halten: Verwenden Sie
@Syntax macht Code klarer und lesbarer - Folgt dem Open-Closed-Prinzip: Offen für Erweiterung, geschlossen für Modifikation
Best Practices für die Verwendung von Decorators:
- Nutzung
functools.wrapsFunktionsmetadaten beibehalten - Die alleinige Verantwortlichkeit des Decorators beibehalten
- Ausnahmen in Decorators ordnungsgemäß behandeln
- Auf die Ausführungsreihenfolge von Decorators achten