Property-Decorators – Fortgeschritten
Teil des Abschnitts Objektorientierte Programmierung der Python-Journey von Coddy. Lektion 31 von 64.
Erweiterte Eigenschaftsdekoratoren ermöglichen eine ausgefeiltere Kontrolle über den Attributzugriff, einschließlich berechneter Eigenschaften, Löschmethoden und einer vollständigen Verwaltung von Eigenschaften.
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 gewöhnliche 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 den vollständigen Funktionsumfang der Property:
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}") # Punktzahl: 2500, Level: 3
del player.score
print(f"Score: {player.score}, Level: {player.level}") # Punktzahl: 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 Property-Dekoratoren ermöglichen berechnete Properties (aus anderen Daten berechnet), das Löschen von Properties mit @property.deleter und die vollständige Kontrolle über das Abrufen, Setzen und Löschen von Attributen. So entstehen intuitive Schnittstellen, während eine strenge Datenvalidierung und Kapselung erhalten bleiben.
Aufgabe
MittelIn dieser Herausforderung implementierst du eine Rectangle-Klasse mit geeigneter Kapselung und Eigenschaftsvalidierung.
rectangle.py– Diese Datei musst du bearbeiten. Sie enthält TODO-Kommentare, die dich bei der Implementierung anleiten.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 die entsprechenden 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 einheitliches 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 eines Tupels:
width, height = dimensions
- Wichtig: Verwende im Setter die Syntax zum Entpacken eines Tupels:
Probier es selbst
from rectangle import Rectangle
# Verarbeitung des Testfalls
test_case = input()
# Test der grundlegenden Funktionalitä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}")
# Die Eigenschaft dimensions testen
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}")
# Mehrere Eigenschaftsoperationen testen
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}")
# Grenzfälle der Validierung von Eigenschaften testen
elif test_case == "validation_edge_cases":
rect = Rectangle(5, 3)
try:
rect.width = 0
except ValueError as e:
print(f"Zero width error: {e}")
# Ein 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}")
# Mehrere Rechtecke testen
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}")
# Jedes Rechteck ändern
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}")
# Leistung bei vielen Operationen testen
elif test_case == "performance_test":
rect = Rectangle(5, 5)
# Viele Eigenschaftsoperationen ausführen
for i in range(1000):
rect.width = i % 10 + 1 # Werte 1-10
rect.height = i % 5 + 1 # Werte 1-5
area = rect.area # Auf berechnete Eigenschaft zugreifen
perimeter = rect.perimeter # Auf berechnete Eigenschaft zugreifen
dims = rect.dimensions # Auf den Getter der Eigenschaft zugreifen
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