I denna handledning lär du dig om Python @property decorator; ett pythoniskt sätt att använda getters och setter i objektorienterad programmering.
Python-programmering ger oss en inbyggd @property
dekoratör som gör användningen av getter och setter mycket enklare i Objektorienterad programmering.
Innan vi går in på detaljer om vad @property
dekoratör är, låt oss först bygga en intuition på varför det skulle behövas i första hand.
Klass utan getter och setter
Låt oss anta att vi bestämmer oss för att skapa en klass som lagrar temperaturen i grader Celsius. Det skulle också implementera en metod för att omvandla temperaturen till grader Fahrenheit. Ett sätt att göra detta är följande:
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32
Vi kan göra objekt ur denna klass och manipulera temperature
attributet som vi vill:
# 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())
Produktion
37 98,60000000000001
De extra decimalerna vid konvertering till Fahrenheit beror på det flytande punkts aritmetiska felet. För att lära dig mer, besök Python Floating Point Arithmetic Error.
När vi tilldelar eller hämtar något objektattribut temperature
som visas ovan, söker Python det i objektets inbyggda __dict__
ordboksattribut.
>>> human.__dict__ ('temperature': 37)
Därför blir man.temperature
internt man.__dict__('temperature')
.
Använda Getters och Setters
Antag att vi vill utöka användbarheten för Celsius-klassen som definierats ovan. Vi vet att temperaturen på något föremål inte kan understiga -273,15 grader Celsius (absolut noll inom termodynamik)
Låt oss uppdatera vår kod för att implementera denna värdebegränsning.
En uppenbar lösning på ovanstående begränsning är att dölja attributet temperature
(göra det privat) och definiera nya getter- och settermetoder för att manipulera det. Detta kan göras enligt följande:
# 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
Som vi kan se introducerar ovanstående metod två nya get_temperature()
och set_temperature()
metoder.
Dessutom temperature
ersattes med _temperature
. En understrykning _
i början används för att beteckna privata variabler i Python.
Låt oss nu använda den här implementeringen:
# 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())
Produktion
37 98.60000000000001 Spårning (senaste samtalet senast): Fil "", rad 30, i fil "", rad 16, i set_temperature ValueError: Temperatur under -273.15 är inte möjlig.
Den här uppdateringen genomförde framgångsrikt den nya begränsningen. Vi får inte längre ställa in temperaturen under -273,15 grader Celsius.
Obs! De privata variablerna finns inte i Python. Det finns helt enkelt normer att följa. Själva språket tillämpar inga begränsningar.
>>> human._temperature = -300 >>> human.get_temperature() -300
Dock är större problem med ovanstående uppdatering som alla program som genomförs vår tidigare klass måste ändra sin kod från obj.temperature
att obj.get_temperature()
och alla uttryck som obj.temperature = val
till obj.set_temperature(val)
.
Denna refactoring kan orsaka problem när du hanterar hundratusentals rader med koder.
Sammantaget var vår nya uppdatering inte bakåtkompatibel. Det är här som @property
kommer att rädda.
Fastighetsklass
Ett pythoniskt sätt att hantera ovanstående problem är att använda property
klassen. Så här kan vi uppdatera vår kod:
# 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)
Vi lade till en print()
funktion inuti get_temperature()
och set_temperature()
att tydligt observera att de körs.
Den sista raden i koden skapar ett fastighetsobjekt temperature
. Enkelt uttryckt fäster egenskapen lite kod ( get_temperature
och set_temperature
) till medlemsattributåtgångarna ( temperature
).
Låt oss använda den här uppdateringskoden:
# 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
Produktion
Inställningsvärde … Få värde … 37 Få värde … 98.60000000000001 Inställningsvärde … Spårning (senaste samtalet senast): Fil "", rad 31, i fil "", rad 18, i set_temperature ValueError: Temperatur under -273 är inte möjlig
Som vi kan se kommer alla koder som hämtar värdet av temperature
automatiskt att ringa get_temperature()
istället för en ordlista (__dict__). På samma sätt kommer alla koder som tilldelar ett värde att temperature
ringa automatiskt set_temperature()
.
Vi kan till och med se ovan som set_temperature()
kallades även när vi skapade ett objekt.
>>> human = Celsius(37) Setting value…
Kan du gissa varför?
Anledningen är att när ett objekt skapas __init__()
kallas metoden. Denna metod har linjen self.temperature = temperature
. Detta uttryck ringer automatiskt set_temperature()
.
På samma sätt, all åtkomst som c.temperature
automatiskt samtal get_temperature()
. Detta är vad egendom gör. Här är några fler exempel.
>>> human.temperature Getting value 37 >>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001
Genom att använda property
kan vi se att ingen ändring krävs i implementeringen av värdebegränsningen. Således är vår implementering bakåtkompatibel.
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 attributefset
is function to set value of the attributefdel
is function to delete the attributedoc
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)
Dessa två koder är likvärdiga.
Programmerare som är bekanta med Python Decorators kan inse att konstruktionen ovan kan implementeras som dekoratörer.
Vi kan inte ens definiera namnen get_temperature
och set_temperature
eftersom de är onödiga och förorenar klassnamnområdet.
För detta återanvänder vi temperature
namnet medan vi definierar våra getter- och setterfunktioner. Låt oss titta på hur man implementerar detta som dekoratör:
# 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)
Produktion
Inställningsvärde … Få värde … 37 Få värde … 98.60000000000001 Inställningsvärde … Spårning (senaste samtalet senast): Fil "", rad 29, i fil "", rad 4, i __init__ fil "", rad 18, i temperatur ValueError: Temperatur under -273 är inte möjlig
Ovanstående implementering är enkel och effektiv. Det är det rekommenderade sättet att använda property
.