Property-Decorators – Fortgeschritten
Teil des Abschnitts Objektorientierte Programmierung der Python-Journey von Coddy. Lektion 31 von 64.
Fortgeschrittene Eigenschaftsdekoratoren ermöglichen eine ausgefeiltere Kontrolle über den Attributzugriff, einschließlich berechneter Eigenschaften, Löschern und der vollständigen Eigenschaftenverwaltung.
Hier ist ein Beispiel für berechnete Eigenschaften, die Werte aus anderen Attributen ableiten:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
@property
def perimeter(self):
return 2 * (self.width + self.height)Verwende berechnete Eigenschaften wie reguläre Attribute:
rect = Rectangle(5, 3)
print(rect.area) # 15 - automatisch berechnet
print(rect.perimeter) # 16 - automatisch berechnetErstelle eine Eigenschaft mit Getter, Setter und Deleter:
class Temperature:
def __init__(self):
self._temp = 0
@property
def temperature(self):
return self._temp
@temperature.setter
def temperature(self, value):
if value < -273.15:
raise ValueError("Temperature below absolute zero!")
self._temp = value
@temperature.deleter
def temperature(self):
print("Resetting temperature to 0")
self._temp = 0Verwende die vollständige property-Funktionalität:
temp = Temperature()Verwende den Setter mit Validierung:
temp.temperature = 25
print(temp.temperature) # 25
# temp.temperature = -300 # Würde ValueError auslösenVerwende den Deleter:
del temp.temperature
print(temp.temperature) # 0Erstelle ein komplexeres Beispiel mit einem Spielstand:
class Player:
def __init__(self, name):
self.name = name
self._score = 0
self._level = 1
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if value >= 0:
self._score = value
self._level = (value // 1000) + 1
else:
raise ValueError("Score cannot be negative")
@score.deleter
def score(self):
print(f"Resetting {self.name}'s progress")
self._score = 0
self._level = 1
@property
def level(self):
return self._level
player = Player("Alice")
player.score = 2500
print(f"Score: {player.score}, Level: {player.level}") # Punktestand: 2500, Level: 3
del player.score
print(f"Score: {player.score}, Level: {player.level}") # Punktestand: 0, Level: 1Ausgabe:
15
16
25
Resetting temperature to 0
0
Score: 2500, Level: 3
Resetting Alice's progress
Score: 0, Level: 1Wichtiger Punkt: Erweiterte Eigenschaftsdekoratoren ermöglichen berechnete Eigenschaften (berechnet aus anderen Daten), das Löschen von Eigenschaften mit @property.deleter sowie die vollständige Kontrolle über das Abrufen, Setzen und Löschen von Attributen. Dadurch entstehen intuitive Schnittstellen, während eine zuverlässige Datenvalidierung und Kapselung erhalten bleiben.
Aufgabe
MittelIn dieser Herausforderung implementierst du eine Rectangle-Klasse mit ordnungsgemäßer Kapselung und Eigenschaftsvalidierung.
rectangle.py– Dies ist die Datei, die du bearbeiten musst. Sie enthält TODO-Kommentare, die dich bei der Implementierung unterstützen.driver.py– Enthält umfangreiche Testszenarien (nicht ändern)
- Implementiere private Attribute für
_widthund_height - Erstelle Eigenschaften für
widthundheightmit Validierung (müssen positiv sein)- Löse geeignete ValueError-Meldungen aus, wie in den TODOs angegeben
- Wichtig: Prüfe, ob der Wert kleiner oder gleich 0 ist, und löse dann den Fehler aus (dies gewährleistet ein konsistentes Verhalten)
- Implementiere schreibgeschützte Eigenschaften für
areaundperimeter - Erstelle eine
dimensions-Eigenschaft mit Getter-, Setter- und Deleter-Funktionalität, wie in den TODOs beschrieben- Wichtig: Verwende im Setter die Syntax zum Entpacken von Tupeln:
width, height = dimensions
- Wichtig: Verwende im Setter die Syntax zum Entpacken von Tupeln:
Probier es selbst
from rectangle import Rectangle
# Testfall-Handler
test_case = input()
# Test der Grundfunktionalität
if test_case == "default_test":
rect = Rectangle(5, 3)
print(f"Width: {rect.width}, Height: {rect.height}")
print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")
# Test the dimensions property
print(f"Dimensions: {rect.dimensions}")
rect.dimensions = (10, 8)
print(f"New area: {rect.area}")
# Validierung testen
try:
rect.width = -2
except ValueError as e:
print(f"Validation error: {e}")
# Deleter testen
del rect.dimensions
print(f"After reset: {rect.dimensions}")
# Test mit Nullwerten
elif test_case == "zero_values":
try:
rect = Rectangle(0, 5)
except ValueError as e:
print(f"Error creating rectangle: {e}")
try:
rect = Rectangle(5, 0)
except ValueError as e:
print(f"Error creating rectangle: {e}")
# Test mit negativen Werten
elif test_case == "negative_values":
rect = Rectangle(5, 3)
original_dimensions = rect.dimensions
try:
rect.dimensions = (5, -3)
except ValueError as e:
print(f"Error setting dimensions: {e}")
print(f"Dimensions after failed update: {rect.dimensions}")
print(f"Original dimensions preserved: {rect.dimensions == original_dimensions}")
# Test mit großen Werten
elif test_case == "large_values":
rect = Rectangle(1000000, 2000000)
print(f"Large rectangle area: {rect.area}")
print(f"Large rectangle perimeter: {rect.perimeter}")
# Test mit Gleitkommawerten
elif test_case == "float_values":
rect = Rectangle(3.5, 2.75)
print(f"Dimensions: {rect.dimensions}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
rect.dimensions = (1.1, 2.2)
print(f"New area with float dimensions: {rect.area}")
# Test mit Typfehlern
elif test_case == "type_errors":
try:
rect = Rectangle("5", 3)
except Exception as e:
print(f"Type error during creation: {type(e).__name__}: {e}")
rect = Rectangle(5, 3)
try:
rect.dimensions = 10 # Kein Tupel
except Exception as e:
print(f"Type error setting dimensions: {type(e).__name__}: {e}")
# Test mehrerer Property-Operationen
elif test_case == "property_operations":
rect = Rectangle(5, 10)
print(f"Initial - Width: {rect.width}, Height: {rect.height}, Area: {rect.area}")
rect.width = 8
print(f"After width change - Width: {rect.width}, Area: {rect.area}")
rect.height = 6
print(f"After height change - Height: {rect.height}, Area: {rect.area}")
rect.dimensions = (12, 9)
print(f"After dimensions change - Dimensions: {rect.dimensions}, Area: {rect.area}")
del rect.dimensions
print(f"After reset - Dimensions: {rect.dimensions}, Area: {rect.area}")
# Test von Grenzfällen der Property-Validierung
elif test_case == "validation_edge_cases":
rect = Rectangle(5, 3)
try:
rect.width = 0
except ValueError as e:
print(f"Zero width error: {e}")
# Sehr kleiner positiver Wert sollte akzeptiert werden
rect.height = 0.0001
print(f"Small height accepted: {rect.height}")
print(f"Area with small height: {rect.area}")
# Teste mehrere Rechtecke
elif test_case == "multiple_rectangles":
rect1 = Rectangle(5, 3)
rect2 = Rectangle(10, 2)
rect3 = Rectangle(4, 4)
print(f"Rectangle 1 - Area: {rect1.area}, Perimeter: {rect1.perimeter}")
print(f"Rectangle 2 - Area: {rect2.area}, Perimeter: {rect2.perimeter}")
print(f"Rectangle 3 - Area: {rect3.area}, Perimeter: {rect3.perimeter}")
# Ändere jedes Rechteck
rect1.width = 7
rect2.height = 5
rect3.dimensions = (6, 6)
print(f"After modifications:")
print(f"Rectangle 1 - Dimensions: {rect1.dimensions}")
print(f"Rectangle 2 - Dimensions: {rect2.dimensions}")
print(f"Rectangle 3 - Dimensions: {rect3.dimensions}")
# Teste die Leistung mit vielen Operationen
elif test_case == "performance_test":
rect = Rectangle(5, 5)
# Führe viele Property-Operationen durch
for i in range(1000):
rect.width = i % 10 + 1 # Werte 1-10
rect.height = i % 5 + 1 # Werte 1-5
area = rect.area # Zugriff auf berechnete Property
perimeter = rect.perimeter # Zugriff auf berechnete Property
dims = rect.dimensions # Zugriff auf Property-Getter
print(f"Final state after 1000 operations:")
print(f"Width: {rect.width}, Height: {rect.height}")
print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")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