Python @property: Wie und warum? - Programiz

In diesem Tutorial lernen Sie Python @property decorator kennen. eine pythonische Möglichkeit, Getter und Setter in der objektorientierten Programmierung zu verwenden.

Die Python-Programmierung bietet uns einen integrierten @propertyDekorator, der die Verwendung von Getter und Setter in der objektorientierten Programmierung erheblich vereinfacht.

Bevor @propertywir uns näher mit dem Thema Dekorateur befassen, wollen wir zunächst eine Vorstellung davon entwickeln, warum er überhaupt benötigt wird.

Klasse ohne Getter und Setter

Nehmen wir an, wir entscheiden uns für eine Klasse, die die Temperatur in Grad Celsius speichert. Es würde auch eine Methode implementieren, um die Temperatur in Grad Fahrenheit umzurechnen. Eine Möglichkeit, dies zu tun, ist wie folgt:

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

Wir können Objekte aus dieser Klasse erstellen und das temperatureAttribut nach Belieben bearbeiten:

 # Basic method of setting and getting attributes in Python class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # Create a new object human = Celsius() # Set the temperature human.temperature = 37 # Get the temperature attribute print(human.temperature) # Get the to_fahrenheit method print(human.to_fahrenheit())

Ausgabe

 37 98.60000000000001

Die zusätzlichen Dezimalstellen bei der Umrechnung in Fahrenheit sind auf den Gleitkomma-Rechenfehler zurückzuführen. Weitere Informationen finden Sie unter Python Floating Point Arithmetic Error.

Immer wenn wir ein Objektattribut wie temperatureoben gezeigt zuweisen oder abrufen , sucht Python es im integrierten __dict__Wörterbuchattribut des Objekts .

 >>> human.__dict__ ('temperature': 37)

Daher wird man.temperatureintern man.__dict__('temperature').

Getter und Setter verwenden

Angenommen, wir möchten die Benutzerfreundlichkeit der oben definierten Celsius-Klasse erweitern. Wir wissen, dass die Temperatur eines Objekts nicht unter -273,15 Grad Celsius liegen kann (absoluter Nullpunkt in der Thermodynamik)

Lassen Sie uns unseren Code aktualisieren, um diese Wertebeschränkung zu implementieren.

Eine offensichtliche Lösung für die obige Einschränkung besteht darin, das Attribut auszublenden temperature(es privat zu machen) und neue Getter- und Setter-Methoden zu definieren, um es zu manipulieren. Dies kann wie folgt erfolgen:

 # Making Getters and Setter methods class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value

Wie wir sehen können, führt das obige Verfahren zwei neue get_temperature()und set_temperature()Methoden.

Darüber hinaus temperaturewurde durch ersetzt _temperature. Ein Unterstrich _am Anfang kennzeichnet private Variablen in Python.

Verwenden wir nun diese Implementierung:

 # Making Getters and Setter methods class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value # Create a new object, set_temperature() internally called by __init__ human = Celsius(37) # Get the temperature attribute via a getter print(human.get_temperature()) # Get the to_fahrenheit method, get_temperature() called by the method itself print(human.to_fahrenheit()) # new constraint implementation human.set_temperature(-300) # Get the to_fahreheit method print(human.to_fahrenheit())

Ausgabe

 37 98.60000000000001 Traceback (letzter Aufruf zuletzt): Datei "", Zeile 30, in Datei "", Zeile 16, in set_temperature ValueError: Temperatur unter -273,15 ist nicht möglich.

Dieses Update hat die neue Einschränkung erfolgreich implementiert. Wir dürfen die Temperatur nicht mehr unter -273,15 Grad Celsius einstellen.

Hinweis : Die privaten Variablen sind in Python nicht vorhanden. Es gibt einfach Normen, die befolgt werden müssen. Die Sprache selbst unterliegt keinen Einschränkungen.

 >>> human._temperature = -300 >>> human.get_temperature() -300

Allerdings ist das größere Problem mit dem obigen Update , dass alle Programme , die unsere bisherige Klasse implementiert haben ihren Code so zu ändern , obj.temperaturezu obj.get_temperature()und alle Ausdrücke wie obj.temperature = valzu obj.set_temperature(val).

Dieses Refactoring kann Probleme beim Umgang mit Hunderttausenden von Codezeilen verursachen.

Alles in allem war unser neues Update nicht abwärtskompatibel. Hier @propertykommt die Rettung.

Die Eigenschaftsklasse

Eine pythonische Methode, um mit dem oben genannten Problem umzugehen, ist die Verwendung der propertyKlasse. So können wir unseren Code aktualisieren:

 # using property class class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # getter def get_temperature(self): print("Getting value… ") return self._temperature # setter def set_temperature(self, value): print("Setting value… ") if value < -273.15: raise ValueError("Temperature below -273.15 is not possible") self._temperature = value # creating a property object temperature = property(get_temperature, set_temperature)

Wir haben eine print()Funktion innerhalb get_temperature()und set_temperature()klar zu beobachten , dass sie ausgeführt werden.

Die letzte Zeile des Codes erstellt ein Eigenschaftsobjekt temperature. Einfach ausgedrückt, die Eigenschaft fügt den Mitgliedsattributzugriffen ( ) Code ( get_temperatureund ) hinzu.set_temperaturetemperature

Verwenden wir diesen Update-Code:

 # using property class class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # getter def get_temperature(self): print("Getting value… ") return self._temperature # setter def set_temperature(self, value): print("Setting value… ") if value < -273.15: raise ValueError("Temperature below -273.15 is not possible") self._temperature = value # creating a property object temperature = property(get_temperature, set_temperature) human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) human.temperature = -300

Ausgabe

 Wert einstellen… Wert abrufen… 37 Wert abrufen… 98.60000000000001 Wert einstellen… Traceback (letzter Aufruf zuletzt): Datei "", Zeile 31, in Datei "", Zeile 18, in set_temperature ValueError: Temperatur unter -273 ist nicht möglich

Wie wir sehen können, wird jeder Code, der den Wert von abruft temperature, automatisch get_temperature()anstelle einer Wörterbuchsuche (__dict__) aufgerufen. Ebenso wird jeder Code, dem ein Wert zugewiesen temperaturewird, automatisch aufgerufen set_temperature().

Wir können sogar oben sehen, dass dies set_temperature()aufgerufen wurde, selbst als wir ein Objekt erstellt haben.

 >>> human = Celsius(37) Setting value… 

Kannst du erraten warum?

Der Grund ist, dass beim Erstellen eines Objekts die __init__()Methode aufgerufen wird. Diese Methode hat die Linie self.temperature = temperature. Dieser Ausdruck ruft automatisch auf set_temperature().

Ebenso c.temperatureruft jeder Zugriff wie automatisch an get_temperature(). Dies ist, was Eigenschaft tut. Hier noch ein paar Beispiele.

 >>> human.temperature Getting value 37 >>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001

Durch die Verwendung propertykönnen wir sehen, dass bei der Implementierung der Wertebeschränkung keine Änderung erforderlich ist. Somit ist unsere Implementierung abwärtskompatibel.

Note: The actual temperature value is stored in the private _temperature variable. The temperature attribute is a property object which provides an interface to this private variable.

The @property Decorator

In Python, property() is a built-in function that creates and returns a property object. The syntax of this function is:

 property(fget=None, fset=None, fdel=None, doc=None)

where,

  • fget is function to get value of the attribute
  • fset is function to set value of the attribute
  • fdel is function to delete the attribute
  • doc is a string (like a comment)

As seen from the implementation, these function arguments are optional. So, a property object can simply be created as follows.

 >>> property() 

A property object has three methods, getter(), setter(), and deleter() to specify fget, fset and fdel at a later point. This means, the line:

 temperature = property(get_temperature,set_temperature)

can be broken down as:

 # make empty property temperature = property() # assign fget temperature = temperature.getter(get_temperature) # assign fset temperature = temperature.setter(set_temperature)

Diese beiden Codes sind äquivalent.

Programmierer, die mit Python Decorators vertraut sind, können erkennen, dass das obige Konstrukt als Decorators implementiert werden kann.

Wir können die Namen sogar nicht definieren get_temperatureund set_temperatureda sie unnötig sind und den Klassennamensraum verschmutzen.

Dazu verwenden wir den temperatureNamen wieder, während wir unsere Getter- und Setter-Funktionen definieren. Schauen wir uns an, wie dies als Dekorateur umgesetzt wird:

 # Using @property decorator class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 @property def temperature(self): print("Getting value… ") return self._temperature @temperature.setter def temperature(self, value): print("Setting value… ") if value < -273.15: raise ValueError("Temperature below -273 is not possible") self._temperature = value # create an object human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) coldest_thing = Celsius(-300)

Ausgabe

 Wert einstellen… Wert abrufen… 37 Wert abrufen… 98.60000000000001 Wert einstellen… Traceback (letzter Aufruf zuletzt): Datei "", Zeile 29, in Datei "", Zeile 4, in __init__ Datei "", Zeile 18, in Temperatur ValueError: Eine Temperatur unter -273 ist nicht möglich

Die obige Implementierung ist einfach und effizient. Dies ist die empfohlene Verwendung property.

Interessante Beiträge...