Python @property decorator
Python @property decorator
In diesem Tutorial lernen Sie Python @property decorator kennen; eine pythonische Art, Getter und Setter in der objektorientierten Programmierung zu verwenden.
Die Python-Programmierung stellt uns einen integrierten @property
zur Verfügung decorator, der die Verwendung von Getter und Setter in der objektorientierten Programmierung viel einfacher macht.
Bevor wir ins Detail gehen, was @property
decorator ist, lassen Sie uns zunächst eine Intuition darüber aufbauen, 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 umzuwandeln. 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 machen und den temperature
manipulieren Attribut nach Belieben:
# 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 Fließkomma-Arithmetikfehler zurückzuführen. Weitere Informationen finden Sie unter Python Floating Point Arithmetic Error.
Wann immer wir ein Objektattribut wie temperature
zuweisen oder abrufen Wie oben gezeigt, sucht Python im integrierten __dict__
des Objekts Dictionary-Attribut.
>>> human.__dict__
{'temperature': 37}
Daher man.temperature
wird intern zu man.__dict__['temperature']
.
Getter und Setter verwenden
Angenommen, wir möchten die Benutzerfreundlichkeit von Celsius erweitern oben definierte Klasse. Wir wissen, dass die Temperatur eines Objekts nicht unter -273,15 Grad Celsius sinken kann (absoluter Nullpunkt in der Thermodynamik)
Lassen Sie uns unseren Code aktualisieren, um diese Wertbeschränkung zu implementieren.
Eine offensichtliche Lösung für die obige Einschränkung ist das Ausblenden des Attributs temperature
(machen Sie es privat) und definieren Sie neue Getter- und Setter-Methoden, 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 die obige Methode zwei neue get_temperature()
ein und set_temperature()
Methoden.
Außerdem temperature
wurde durch _temperature
ersetzt . Ein Unterstrich _
am Anfang wird verwendet, um private Variablen in Python zu bezeichnen.
Lassen Sie uns nun diese Implementierung verwenden:
# 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 (most recent call last): File "<string>", line 30, in <module> File "<string>", line 16, in set_temperature ValueError: Temperature below -273.15 is not possible.
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 existieren in Python nicht wirklich. Es gibt einfach Normen, die eingehalten werden müssen. Die Sprache selbst unterliegt keinerlei Beschränkungen.
>>> human._temperature = -300
>>> human.get_temperature()
-300
Das größere Problem bei dem obigen Update ist jedoch, dass alle Programme, die unsere vorherige Klasse implementiert haben, ihren Code von obj.temperature
ändern müssen bis obj.get_temperature()
und alle Ausdrücke wie obj.temperature = val
bis 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 ist @property
kommt zur Rettung.
Die Eigenschaftsklasse
Ein pythonischer Weg, um das obige Problem zu lösen, ist die Verwendung von property
Klasse. 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 einen print()
hinzugefügt Funktion in get_temperature()
und set_temperature()
klar zu erkennen, dass sie hingerichtet werden.
Die letzte Zeile des Codes erstellt ein Eigenschaftsobjekt temperature
. Einfach ausgedrückt, hängt eine Eigenschaft Code an (get_temperature
und set_temperature
) auf die Elementattributzugriffe (temperature
).
Verwenden wir diesen Aktualisierungscode:
# 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
Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): File "<string>", line 31, in <module> File "<string>", line 18, in set_temperature ValueError: Temperature below -273 is not possible
Wie wir sehen können, jeder Code, der den Wert von temperature
abruft ruft automatisch get_temperature()
an anstelle einer Wörterbuchsuche (__dict__). Ebenso jeder Code, der temperature
einen Wert zuweist ruft automatisch set_temperature()
an .
Wir können sogar darüber set_temperature()
sehen wurde aufgerufen, selbst wenn wir ein Objekt erstellt haben.
>>> human = Celsius(37)
Setting value...
Kannst du erraten, warum?
Der Grund dafür ist, dass beim Erstellen eines Objekts der __init__()
Methode aufgerufen wird. Diese Methode hat die Zeile self.temperature = temperature
. Dieser Ausdruck ruft automatisch set_temperature()
auf .
Ebenso jeder Zugriff wie c.temperature
ruft automatisch get_temperature()
auf . Dies ist, was Eigentum tut. Hier sind ein paar weitere Beispiele.
>>> human.temperature
Getting value
37
>>> human.temperature = 37
Setting value
>>> c.to_fahrenheit()
Getting value
98.60000000000001
Durch die Verwendung von property
, können wir sehen, dass bei der Implementierung der Wertbeschränkung keine Änderung erforderlich ist. Somit ist unsere Implementierung abwärtskompatibel.
Hinweis :Der aktuelle Temperaturwert wird im privaten _temperature
gespeichert Variable. Der temperature
Das Attribut ist ein Eigenschaftsobjekt, das eine Schnittstelle zu dieser privaten Variablen bereitstellt.
Der @property Decorator
In Python property()
ist eine integrierte Funktion, die einen property
erstellt und zurückgibt Objekt. Die Syntax dieser Funktion ist:
property(fget=None, fset=None, fdel=None, doc=None)
wo,
fget
ist eine Funktion, um den Wert des Attributs abzurufenfset
ist eine Funktion, um den Wert des Attributs zu setzenfdel
ist eine Funktion zum Löschen des Attributsdoc
ist ein String (wie ein Kommentar)
Wie aus der Implementierung ersichtlich, sind diese Funktionsargumente optional. Ein Eigenschaftsobjekt kann also einfach wie folgt erstellt werden.
>>> property()
<property object at 0x0000000003239B38>
Ein Eigenschaftsobjekt hat drei Methoden, getter()
, setter()
, und deleter()
um fget
anzugeben , fset
und fdel
zu einem späteren Zeitpunkt. Das heißt, die Zeile:
temperature = property(get_temperature,set_temperature)
kann wie folgt aufgeschlüsselt werden:
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
Diese beiden Codes sind gleichwertig.
Programmierer, die mit Python Decorators vertraut sind, werden erkennen, dass das obige Konstrukt als Decorators implementiert werden kann.
Wir können nicht einmal die Namen get_temperature
definieren und set_temperature
da sie unnötig sind und den Klassennamensraum verschmutzen.
Dafür verwenden wir wieder den temperature
name beim Definieren unserer Getter- und Setter-Funktionen. Sehen wir uns an, wie dies als Dekorateur implementiert 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
Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): File "", line 29, in File " ", line 4, in __init__ File " ", line 18, in temperature ValueError: Temperature below -273 is not possible
Die obige Implementierung ist einfach und effizient. Dies ist die empfohlene Methode zur Verwendung von property
.
Python