Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial programming >> Python

Pythons @property Decorator meistern: Saubere, rückwärtskompatible Getter & Setter

Pythons @property Decorator meistern

Entdecken Sie, wie Pythons eingebauter @property Decorator es Ihnen erlaubt, prägnante, wartbare Getter und Setter zu schreiben, während Ihre API rückwärtskompatibel bleibt.

Pythons @property Decorator ist ein mächtiges Werkzeug zur Kapselung von Attributzugriff. Er ermöglicht es Ihnen, benutzerdefinierte Logik zu definieren, die immer dann ausgeführt wird, wenn ein Attribut gelesen oder geschrieben wird, ohne dass Aufrufer ihren Code ändern müssen. In den folgenden Abschnitten gehen wir durch ein reales Beispiel – Temperaturkonvertierungen – zeigen, wie man Constraints erzwingt, und erklären, warum @property die idiomatische Lösung ist.


Eine Klasse ohne Getter und Setter

Angenommen, wir brauchen eine einfache Klasse, die Temperatur in Celsius speichert und in Fahrenheit umrechnen kann:

class Celsius:
    def __init__(self, temperatur=0):
        self.temperatur = temperatur
    def to_fahrenheit(self):
        return (self.temperatur * 1.8) + 32

Die Verwendung ist unkompliziert:

# Neues Objekt erstellen
mensch = Celsius()
# Temperatur setzen
mensch.temperatur = 37
# Temperatur lesen
print(mensch.temperatur)
# In Fahrenheit umrechnen
print(mensch.to_fahrenheit())

Ausgabe

37 98.60000000000001

Das Gleitkommaergebnis ist aufgrund der binären Darstellung von Dezimalzahlen zu erwarten. Wenn Sie mensch.__dict__ inspizieren, sehen Sie, dass Python den Wert in einem einfachen Dictionary-Eintrag speichert:

>>> mensch.__dict__
{'temperatur': 37}

Constraints mit Gettern und Settern hinzufügen

Echte Objekte benötigen oft Validierung. Zum Beispiel können Temperaturen nicht unter den absoluten Nullpunkt (-273,15 °C) fallen. Ein gerader Weg ist, das Attribut zu verstecken und explizite Getter/Setter-Methoden bereitzustellen:

class Celsius:
    def __init__(self, temperatur=0):
        self.set_temperatur(temperatur)
    def to_fahrenheit(self):
        return (self.get_temperatur() * 1.8) + 32
    # Getter
    def get_temperatur(self):
        return self._temperatur
    # Setter
    def set_temperatur(self, wert):
        if wert < -273.15:
            raise ValueError("Temperatur unter -273,15 ist nicht möglich.")
        self._temperatur = wert

Jetzt erzwingt die Klasse die untere Schranke, aber jeder bestehende Code, der auf obj.temperatur zugriff, muss aktualisiert werden, um obj.get_temperatur() und obj.set_temperatur(val) aufzurufen. Das ist ein großer Refactoring-Aufwand für große Codebasen.

Zudem sind Pythons "private" Variablen nur eine Namenskonvention – _temperatur kann immer noch direkt zugegriffen werden, was die Kapselung bricht. Hier glänzt @property.


Die property Klasse verwenden

Python bietet eine property-Klasse, die Getter- und Setter-Funktionen an einen Attributnamen bindet:

class Celsius:
    def __init__(self, temperatur=0):
        self.temperatur = temperatur
    def to_fahrenheit(self):
        return (self.temperatur * 1.8) + 32
    # Getter
    def get_temperatur(self):
        print("Wert wird gelesen...")
        return self._temperatur
    # Setter
    def set_temperatur(self, wert):
        print("Wert wird gesetzt...")
        if wert < -273.15:
            raise ValueError("Temperatur unter -273,15 ist nicht möglich")
        self._temperatur = wert
# Als Property exponieren
temperatur = property(get_temperatur, set_temperatur)

Mit diesem Setup verhält sich der folgende Code genau wie die ursprüngliche einfache Klasse, aber mit Validierung hinter den Kulissen:

mensch = Celsius(37)
print(mensch.temperatur)  # löst Getter aus
print(mensch.to_fahrenheit())  # nutzt Getter intern
mensch.temperatur = -300  # löst Setter aus und wirft ValueError

Ausgabe

Wert wird gesetzt... Wert wird gelesen... 37 Wert wird gelesen... 98.60000000000001 Wert wird gesetzt... Traceback (most recent call last): ... ValueError: Temperatur unter -273 ist nicht möglich

Beachten Sie, wie der Code, der auf temperatur zugreift, unverändert bleibt; nur die zugrundeliegende Implementierung erhielt Validierung.

Tipp: Die eigentlichen Daten liegen in _temperatur; temperatur ist nur eine bequeme, sichere Schnittstelle.


Der @property Decorator

Während die property-Klasse funktioniert, bietet Pythons Decorator-Syntax einen saubereren, lesbareren Weg zum gleichen Ergebnis. Der Decorator erstellt automatisch das Property-Objekt und bindet Getter und Setter an den Attributnamen.

class Celsius:
    def __init__(self, temperatur=0):
        self.temperatur = temperatur
    def to_fahrenheit(self):
        return (self.temperatur * 1.8) + 32
    @property
    def temperatur(self):
        print("Wert wird gelesen...")
        return self._temperatur
    @temperatur.setter
    def temperatur(self, wert):
        print("Wert wird gesetzt...")
        if wert < -273.15:
            raise ValueError("Temperatur unter -273 ist nicht möglich")
        self._temperatur = wert

Die Verwendung bleibt dieselbe:

mensch = Celsius(37)
print(mensch.temperatur)
print(mensch.to_fahrenheit())
# Versuch, ungültige Temperatur zu setzen
try:
    Celsius(-300)
except ValueError as e:
    print(e)

Ausgabe

Wert wird gesetzt... Wert wird gelesen... 37 Wert wird gelesen... 98.60000000000001 Wert wird gesetzt... Traceback (most recent call last): ... ValueError: Temperatur unter -273 ist nicht möglich

Die Verwendung von @property hält den Namespace aufgeräumt, eliminiert die Notwendigkeit separater Getter/Setter-Methodennamen und ist das empfohlene Pattern für die meisten Python-Entwickler.

Python

  1. Python-Datentypen meistern: Praxisleitfaden
  2. Python-Operatoren meistern: Umfassender Leitfaden
  3. Python pass-Statement meistern: Praxisleitfaden
  4. Python-Funktionsargumente meistern: Positional, Keyword & Default-Parameter
  5. Python-Dictionaries meistern: Erstellung, Manipulation und fortgeschrittene Techniken
  6. Python Vererbung meistern: Konzepte, Syntax und praktische Beispiele
  7. Operator Overloading in Python meistern: Ein praktischer Leitfaden
  8. Python Iteratoren meistern: Funktionsweise und Erstellung eigener Iteratoren
  9. Python Closures erklärt: Wie verschachtelte Funktionen Variablen erfassen
  10. Pythons @property Decorator meistern: Saubere, rückwärtskompatible Getter & Setter