Menu
Coddy logo textTech

Property-Decorators für Fortgeschrittene

Teil des Abschnitts Object Oriented Programming der Python-Journey von Coddy — Lektion 31 von 64.

Fortgeschrittene Property-Decorators bieten eine ausgefeiltere Kontrolle über den Attributzugriff, einschließlich berechneter Eigenschaften, Deleter und vollständiger Eigenschaftsverwaltung.

Hier ist ein Beispiel für berechnete Eigenschaften, die Werte von 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)

Verwenden Sie berechnete Eigenschaften wie reguläre Attribute:

rect = Rectangle(5, 3)
print(rect.area)      # 15 - automatisch berechnet
print(rect.perimeter) # 16 - automatisch berechnet

Erstellen Sie 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 = 0

Nutzen Sie die volle Property-Funktionalität:

temp = Temperature()

Verwenden Sie den Setter mit Validierung:

temp.temperature = 25
print(temp.temperature)  # 25

# temp.temperature = -300  # Würde einen ValueError auslösen

Verwenden Sie den Deleter:

del temp.temperature
print(temp.temperature)  # 0

Erstellen Sie 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}")  # Score: 2500, Level: 3

del player.score
print(f"Score: {player.score}, Level: {player.level}")  # Score: 0, Level: 1

Ausgabe:

15
16
25
Resetting temperature to 0
0
Score: 2500, Level: 3
Resetting Alice's progress
Score: 0, Level: 1

Kernpunkt: Fortgeschrittene Property-Dekoratoren 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. Dies schafft intuitive Schnittstellen, während gleichzeitig eine starke Datenvalidierung und Kapselung beibehalten wird.

challenge icon

Aufgabe

Mittel

In dieser Herausforderung implementieren Sie eine Rectangle-Klasse mit ordnungsgemäßer Kapselung und Eigenschaftsvalidierung.

  • rectangle.py - Dies ist die Datei, die Sie bearbeiten müssen; sie enthält TODO-Kommentare, die Sie bei der Implementierung unterstützen
  • driver.py - Enthält umfangreiche Testszenarien (nicht ändern)
  1. Implementieren Sie private Attribute für _width und _height
  2. Erstellen Sie Eigenschaften für width und height mit Validierung (müssen positiv sein)
    • Lösen Sie entsprechende ValueError-Meldungen aus, wie in den TODOs angegeben
    • Wichtig: Prüfen Sie, ob der Wert kleiner oder gleich 0 ist, und lösen Sie dann den Fehler aus (dies stellt ein konsistentes Verhalten sicher)
  3. Implementieren Sie schreibgeschützte Eigenschaften für area und perimeter
  4. Erstellen Sie eine dimensions-Eigenschaft mit Getter-, Setter- und Deleter-Funktionalität, wie in den TODOs beschrieben
    • Wichtig: Verwenden Sie im Setter die Syntax für das Entpacken von Tupeln: width, height = dimensions

Spickzettel

Fortgeschrittene Property-Dekoratoren bieten eine anspruchsvolle Kontrolle über den Attributzugriff mit berechneten Eigenschaften, Deletern und vollständiger Eigenschaftsverwaltung.

Berechnete Eigenschaften:

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)

Vollständige 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 = 0

Verwendung:

# Berechnete Eigenschaften
rect = Rectangle(5, 3)
print(rect.area)      # 15
print(rect.perimeter) # 16

# Vollständige Eigenschaftsfunktionalität
temp = Temperature()
temp.temperature = 25  # Verwendet Setter
print(temp.temperature)  # Verwendet Getter

del temp.temperature   # Verwendet Deleter

Probier es selbst

from rectangle import Rectangle

# Testfall-Handler
test_case = input()

# Grundlegender Funktionstest
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}")
    
    # Teste die dimensions-Eigenschaft
    print(f"Dimensions: {rect.dimensions}")
    rect.dimensions = (10, 8)
    print(f"New area: {rect.area}")
    
    # Teste Validierung
    try:
        rect.width = -2
    except ValueError as e:
        print(f"Validation error: {e}")
    
    # Teste Deleter
    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 Fließkommawerten
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}")

# Teste mehrere Eigenschaftsoperationen
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}")

# Teste Grenzfälle der Eigenschaftsvalidierung
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}")
    
    # Modifiziere 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 Performance mit vielen Operationen
elif test_case == "performance_test":
    rect = Rectangle(5, 5)
    
    # Führe viele Eigenschaftsoperationen aus
    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 Eigenschaft
        perimeter = rect.perimeter  # Zugriff auf berechnete Eigenschaft
        dims = rect.dimensions  # Zugriff auf Eigenschafts-Getter
    
    print(f"Final state after 1000 operations:")
    print(f"Width: {rect.width}, Height: {rect.height}")
    print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Object Oriented Programming