Klassendekoratoren
Teil des Abschnitts Object Oriented Programming der Python-Journey von Coddy — Lektion 40 von 64.
Klassen-Dekoratoren ermöglichen es Ihnen, Klassen zu modifizieren oder zu erweitern, indem sie mit einer anderen Funktion umschlossen werden. Sie funktionieren wie Funktions-Dekoratoren, werden aber auf ganze Klassen angewendet.
Hier ist eine einfache Klasse ohne Dekoration:
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Erstellen Sie einen Klassen-Dekorator, der eine neue Methode hinzufügt:
def add_farewell(cls):
def farewell(self):
return f"Goodbye from {self.name}"
cls.farewell = farewell # Füge die Methode der Klasse hinzu
return clsWenden Sie den Decorator auf eine Klasse an, indem Sie @ verwenden:
@add_farewell
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Jetzt verfügt die Klasse sowohl über die ursprünglichen als auch über die hinzugefügten Methoden:
person = EnhancedPerson("Alice")
print(person.greet()) # Hallo, mein Name ist Alice
print(person.farewell()) # Auf Wiedersehen von AliceSie können auch eine bestehende Methode kapseln – indem Sie das Original speichern, damit Sie es innerhalb des Wrappers weiterhin aufrufen können. Dies ist nützlich, wenn Sie Verhalten (wie Tracking oder Logging) um eine Methode herum hinzufügen möchten, die die Klasse bereits besitzt:
def add_tracking(cls):
original_greet = cls.greet # Die ursprüngliche Methode speichern
def tracked_greet(self):
print(f"greet was called") # Zusätzliches Verhalten davor
return original_greet(self) # Die ursprüngliche Methode aufrufen
cls.greet = tracked_greet # Die Methode in der Klasse ersetzen
return cls
@add_tracking
class TrackedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Wenn Sie greet jetzt aufrufen, wird zuerst der Wrapper ausgeführt und delegiert dann an das Original:
person = TrackedPerson("Alice")
print(person.greet())
# greet wurde aufgerufen
# Hallo, mein Name ist AliceDie wichtigsten Schritte zum Umschließen einer bestehenden Methode sind:
1. Speichern Sie die ursprüngliche Methode: original = cls.method
2. Definieren Sie eine neue Funktion, die original(self) sowie zusätzliche Logik aufruft.
3. Weisen Sie die neue Funktion wieder der Klasse zu: cls.method = new_function
4. Geben Sie die modifizierte Klasse zurück.
Wichtiger Punkt: Klassen-Dekoratoren nehmen eine Klasse als Eingabe entgegen, modifizieren oder erweitern sie und geben die modifizierte Klasse zurück. Sie können Methoden hinzufügen, Attribute ändern oder bestehende Funktionalität umschließen. Verwenden Sie @decorator_name über der Klassendefinition, um sie anzuwenden. Dies bietet eine saubere Möglichkeit, die Klassenfunktionalität ohne Vererbung zu erweitern.
Aufgabe
MittelIn dieser Herausforderung implementieren Sie einen Klassen-Dekorator, der eine Funktionalität zur Verfolgung von Methodenaufrufen hinzufügt.
decorator.py- Implementieren Sie denadd_counterKlassen-Dekoratorcalculator.py- Enthält die Klassen, die Ihren Dekorator verwenden werden
Die Datei driver.py enthält umfangreiche Testszenarien, die Ihre Implementierung gegen verschiedene Anwendungsfälle, Vererbungsmuster und Grenzfälle validieren. Sie müssen diese Datei nicht ändern, aber ihre Untersuchung wird Ihnen helfen, das erwartete Verhalten zu verstehen.
Spickzettel
Klassen-Dekoratoren modifizieren oder erweitern Klassen, indem sie diese mit einer anderen Funktion umhüllen. Sie nehmen eine Klasse als Eingabe, modifizieren sie und geben die modifizierte Klasse zurück.
Grundlegende Syntax für Klassen-Dekoratoren:
def decorator_name(cls):
# Modifiziere die Klasse
return cls
@decorator_name
class MyClass:
passBeispiel – Hinzufügen einer Methode zu einer Klasse:
def add_farewell(cls):
def farewell(self):
return f"Goodbye from {self.name}"
cls.farewell = farewell # Füge die Methode der Klasse hinzu
return cls
@add_farewell
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"
person = EnhancedPerson("Alice")
print(person.greet()) # Hello, my name is Alice
print(person.farewell()) # Goodbye from AliceBeispiel – Umhüllen einer bestehenden Methode (Original speichern, dann ersetzen):
def loud_greet(cls):
original_greet = cls.greet # Speichere die ursprüngliche Methode
def new_greet(self):
result = original_greet(self) # Rufe das Original auf
return result.upper() # Erweitere das Ergebnis
cls.greet = new_greet # Ersetze durch die umhüllte Version
return cls
@loud_greet
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"
person = EnhancedPerson("Alice")
print(person.greet()) # HELLO, MY NAME IS ALICEWichtige Schritte zum Umhüllen einer bestehenden Methode:
original = cls.method_name — Speichern der ursprünglichen Methodedef new_method(self): original(self) — Aufruf innerhalb des Wrapperscls.method_name = new_method — Ersetzen der Methode in der KlasseKlassen-Dekoratoren können Methoden hinzufügen, Attribute modifizieren oder bestehende Funktionalitäten umhüllen, ohne Vererbung zu nutzen.
Probier es selbst
from calculator import Calculator
# Umfassender Testfall-Handler
test_case = input()
if test_case == "basic_functionality":
calc = Calculator()
print(calc.add(5, 3)) # Sollte 8 ausgeben
print(calc.add(2, 7)) # Sollte 9 ausgeben
print(calc.subtract(10, 4)) # Sollte 6 ausgeben
print(calc.call_counts) # Sollte {'add': 2, 'subtract': 1} ausgeben
elif test_case == "multiple_instances":
calc1 = Calculator()
calc2 = Calculator()
print(calc1.add(5, 3)) # Sollte 8 ausgeben
print(calc2.add(2, 7)) # Sollte 9 ausgeben
print(calc1.subtract(10, 4)) # Sollte 6 ausgeben
# Beide Instanzen sollten die gleichen call_counts teilen
print(calc1.call_counts) # Sollte {'add': 2, 'subtract': 1} ausgeben
print(calc2.call_counts) # Sollte {'add': 2, 'subtract': 1} ausgeben
print(calc1.call_counts is calc2.call_counts) # Sollte True ausgeben
elif test_case == "method_call_order":
calc = Calculator()
print(calc.add(1, 2)) # Sollte 3 ausgeben
print(calc.subtract(5, 2)) # Sollte 3 ausgeben
print(calc.add(10, 20)) # Sollte 30 ausgeben
print(calc.add(7, 3)) # Sollte 10 ausgeben
print(calc.call_counts) # Sollte {'add': 3, 'subtract': 1} ausgeben
elif test_case == "large_values":
calc = Calculator()
print(calc.add(1000000, 2000000)) # Sollte 3000000 ausgeben
print(calc.subtract(5000000, 2000000)) # Sollte 3000000 ausgeben
print(calc.call_counts) # Sollte {'add': 1, 'subtract': 1} ausgeben
elif test_case == "negative_values":
calc = Calculator()
print(calc.add(-5, -3)) # Sollte -8 ausgeben
print(calc.subtract(-10, -4)) # Sollte -6 ausgeben
print(calc.call_counts) # Sollte {'add': 1, 'subtract': 1} ausgeben
elif test_case == "float_values":
calc = Calculator()
print(calc.add(5.5, 3.2)) # Sollte 8.7 ausgeben
print(calc.subtract(10.5, 4.2)) # Sollte 6.3 ausgeben
print(calc.call_counts) # Sollte {'add': 1, 'subtract': 1} ausgeben
elif test_case == "zero_values":
calc = Calculator()
print(calc.add(0, 0)) # Sollte 0 ausgeben
print(calc.subtract(0, 0)) # Sollte 0 ausgeben
print(calc.call_counts) # Sollte {'add': 1, 'subtract': 1} ausgeben
elif test_case == "counter_reset":
calc = Calculator()
print(calc.add(5, 3)) # Sollte 8 ausgeben
print(calc.call_counts) # Sollte {'add': 1, 'subtract': 0} ausgeben
# Den Zähler zurücksetzen
calc.call_counts = {"add": 0, "subtract": 0}
print(calc.add(2, 7)) # Sollte 9 ausgeben
print(calc.call_counts) # Sollte {'add': 0, 'subtract': 0} ausgeben
elif test_case == "counter_manipulation":
calc = Calculator()
print(calc.add(5, 3)) # Sollte 8 ausgeben
print(calc.call_counts) # Sollte {'add': 1, 'subtract': 0} ausgeben
# Den Zähler direkt manipulieren
calc.call_counts["add"] = 100
print(calc.add(2, 7)) # Sollte 9 ausgeben
print(calc.call_counts) # Sollte {'add': 101, 'subtract': 0} ausgeben
elif test_case == "performance_test":
calc = Calculator()
# 1000 Additionsoperationen durchführen
for i in range(1000):
calc.add(i, i+1)
# 500 Subtraktionsoperationen durchführen
for i in range(500):
calc.subtract(i+10, i)
print(calc.call_counts) # Sollte {'add': 1000, 'subtract': 500} ausgebenDiese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Object Oriented Programming
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDer self-ParameterMethodenAttributeKonstruktor-Methode (__init__)Zusammenfassung – Einfacher Taschenrechner4Vererbung
Grundlagen der VererbungDie super()-FunktionMethoden überschreibenMehrfachvererbungMethod Resolution OrderZusammenfassung - Mitarbeiter-Hierarchie7Spezielle Methoden
Einführung in Magic MethodsOperator-ÜberladungMagic Methods für ContainerRückblick - Eigene Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster13Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung2Dekoratoren
Einführung in DekoratorenProperty-DekoratorStatischer Methoden-DekoratorKlassenmethoden-Dekorator5Polymorphismus
Methoden-Überschreiben vertieftDuck TypingAbstrakte Klassen und MethodenInterface-DesignZusammenfassung – Formen-Rechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungMixinsStatische und KlassenmethodenKlassendekoratorenContext Manager3Klasseneigenschaften
Instanz- vs. KlassenvariablenProperty-DekoratorenPrivate AttributeZusammenfassung – Bankkonto-Manager6Kapselung
Public, Protected, Private MemberZugriffsmodifikatorenInformation HidingProperty-Decorators für FortgeschritteneZusammenfassung - Studentenverwaltungssystem12Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen