Klassen-Dekoratoren
Teil des Abschnitts Objektorientierte Programmierung der Python-Journey von Coddy. Lektion 40 von 64.
Klassen-Dekoratoren ermöglichen es dir, Klassen zu verändern oder zu erweitern, indem du sie mit einer anderen Funktion umschließt. Sie funktionieren wie Funktionsdekoratoren, 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}"Erstelle einen Klassendekorator, 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 zur Klasse hinzu
return clsWende den Decorator mithilfe von @ auf eine Klasse an:
@add_farewell
class EnhancedPerson:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"Jetzt hat die Klasse sowohl die ursprünglichen als auch die hinzugefügten Methoden:
person = EnhancedPerson("Alice")
print(person.greet()) # Hallo, mein Name ist Alice
print(person.farewell()) # Auf Wiedersehen von AliceDu kannst auch eine vorhandene Methode umschließen: Speichere das Original, damit du es weiterhin innerhalb des Wrappers aufrufen kannst. Das ist nützlich, wenn du Verhalten (wie Tracking oder Protokollierung) zu einer Methode hinzufügen möchtest, die die Klasse bereits besitzt:
def add_tracking(cls):
original_greet = cls.greet # Speichere die ursprüngliche Methode
def tracked_greet(self):
print(f"greet was called") # Zusätzliches Verhalten vorher
return original_greet(self) # Rufe die ursprüngliche Methode auf
cls.greet = tracked_greet # Ersetze die Methode auf der Klasse
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 du jetzt greet aufrufst, wird zuerst der Wrapper ausgeführt und anschließend an das Original delegiert:
person = TrackedPerson("Alice")
print(person.greet())
# greet wurde aufgerufen
# Hallo, mein Name ist AliceDie wichtigsten Schritte zum Umschließen einer vorhandenen Methode sind:
1. Die ursprüngliche Methode speichern: original = cls.method
2. Eine neue Funktion definieren, die original(self) sowie zusätzliche Logik aufruft.
3. Die neue Funktion wieder der Klasse zuweisen: cls.method = new_function
4. Die geänderte Klasse zurückgeben.
Wichtiger Punkt: Klassendekoratoren nehmen eine Klasse als Eingabe, verändern oder erweitern sie und geben die veränderte Klasse zurück. Sie können Methoden hinzufügen, Attribute verändern oder vorhandene Funktionalität umschließen. Verwende @decorator_name über der Klassendefinition, um sie anzuwenden. Dies bietet eine übersichtliche Möglichkeit, die Funktionalität einer Klasse ohne Vererbung zu erweitern.
Aufgabe
MittelIn dieser Herausforderung implementierst du einen Klassendekorator, der eine Funktionalität zur Nachverfolgung von Methodenaufrufen hinzufügt.
decorator.py– Implementiere den Klassendekoratoradd_countercalculator.py– Enthält die Klassen, die deinen Dekorator verwenden werden
Die Datei driver.py enthält umfangreiche Testszenarien, die deine Implementierung anhand verschiedener Anwendungsfälle, Vererbungsmuster und Sonderfälle überprüfen. Du musst diese Datei nicht ändern, aber ihre Untersuchung hilft dir, das erwartete Verhalten zu verstehen.
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) # Should print {'add': 2, 'subtract': 1}
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 dieselben call_counts teilen
print(calc1.call_counts) # Should print {'add': 2, 'subtract': 1}
print(calc2.call_counts) # Should print {'add': 2, 'subtract': 1}
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) # Should print {'add': 3, 'subtract': 1}
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) # Should print {'add': 1, 'subtract': 1}
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) # Should print {'add': 1, 'subtract': 1}
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) # Should print {'add': 1, 'subtract': 1}
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) # Should print {'add': 1, 'subtract': 1}
elif test_case == "counter_reset":
calc = Calculator()
print(calc.add(5, 3)) # Sollte 8 ausgeben
print(calc.call_counts) # Should print {'add': 1, 'subtract': 0}
# Zähler zurücksetzen
calc.call_counts = {"add": 0, "subtract": 0}
print(calc.add(2, 7)) # Sollte 9 ausgeben
print(calc.call_counts) # Should print {'add': 0, 'subtract': 0}
elif test_case == "counter_manipulation":
calc = Calculator()
print(calc.add(5, 3)) # Sollte 8 ausgeben
print(calc.call_counts) # Should print {'add': 1, 'subtract': 0}
# Zähler direkt manipulieren
calc.call_counts["add"] = 100
print(calc.add(2, 7)) # Sollte 9 ausgeben
print(calc.call_counts) # Should print {'add': 101, 'subtract': 0}
elif test_case == "performance_test":
calc = Calculator()
# 1000 add-Operationen durchführen
for i in range(1000):
calc.add(i, i+1)
# 500 subtract-Operationen durchführen
for i in range(500):
calc.subtract(i+10, i)
print(calc.call_counts) # Should print {'add': 1000, 'subtract': 500}Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDer self-ParameterMethodenAttributeKonstruktormethode (__init__)Zusammenfassung – Einfacher Taschenrechner4Vererbung
Grundlegende VererbungDie super()-FunktionMethodenüberschreibungMehrfachvererbungMethodenauflösungsreihenfolgeWiederholung – Mitarbeiterhierarchie7Spezielle Methoden
Einführung in magische MethodenOperatorüberladungMagische Methoden für ContainerRückblick – Benutzerdefinierte Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster13Abschließende Herausforderungen
E-Learning-PlattformBankensystemEntwicklung von SpielfigurenFahrzeugvermietungsservice2Dekoratoren
Einführung in DekoratorenProperty-DekoratorDekorator für statische MethodenDekorator für Klassenmethoden5Polymorphie
Methodenüberschreibung erneut betrachtetDuck-TypingAbstrakte Klassen und MethodenSchnittstellendesignRückblick – Formrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungMixinsStatische Methoden und KlassenmethodenKlassen-DekoratorenKontextmanager3Klassenattribute
Instanz- vs. KlassenvariablenProperty-DecoratorsPrivate AttributeWiederholung – Bankkontoverwaltung6Kapselung
Öffentliche, geschützte, private MemberZugriffsmodifikatorenInformationsverbergungProperty-Decorators – FortgeschrittenWiederholung – System zur Verwaltung von Studentendaten12Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und BenutzerklassenAusleihsystemSuchfunktionAdministrationsoberflächeTests und IntegrationÜbe selbstständig: Online-Python-Compiler