Šajā apmācībā jūs uzzināsit par Python @property dekoratoru; pitonisks veids, kā izmantot getters un seterus objektorientētā programmēšanā.
Python programmēšana nodrošina mūs ar iebūvētu @property
dekoratoru, kas padara objektorientētā programmēšanā daudz vieglāku getter un seters lietošanu.
Pirms iedziļināties detaļās par to, kas ir @property
dekorators, vispirms izveidosim intuīciju par to, kāpēc tas vispār būtu vajadzīgs.
Klase bez geteriem un seteriem
Pieņemsim, ka mēs nolemjam izveidot klasi, kurā temperatūra tiek uzglabāta grādos pēc Celsija. Tas arī ieviestu metodi temperatūras pārvēršanai grādos pēc Fārenheita. Viens veids, kā to izdarīt, ir šāds:
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32
Mēs varam izgatavot objektus no šīs klases un manipulēt ar temperature
atribūtu, kā mēs vēlamies:
# 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())
Rezultāts
37 98.60000000000001
Papildu cipari aiz komata, pārvēršot Fārenheitā, ir saistīti ar peldošā komata aritmētisko kļūdu. Lai uzzinātu vairāk, apmeklējiet Python peldošā komata aritmētisko kļūdu.
Ikreiz, kad mēs piešķiram vai izgūstam kādu objekta atribūtu, piemēram, temperature
kā parādīts iepriekš, Python to meklē objekta iebūvētajā __dict__
vārdnīcas atribūtā.
>>> human.__dict__ ('temperature': 37)
Tāpēc man.temperature
iekšēji kļūst man.__dict__('temperature')
.
Izmantojot Getters un Setters
Pieņemsim, ka mēs vēlamies paplašināt iepriekš definētās Celsija klases lietojamību. Mēs zinām, ka jebkura objekta temperatūra nevar sasniegt zemāku par -273,15 grādiem pēc Celsija (absolūtā nulle termodinamikā)
Atjaunināsim kodu, lai ieviestu šo vērtības ierobežojumu.
Acīmredzams iepriekš minētā ierobežojuma risinājums būs atribūta paslēpšana temperature
(padarot to privātu) un jaunu getter un setter metožu definēšana, lai ar to manipulētu. To var izdarīt šādi:
# 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
Kā redzam, iepriekš minētā metode ievieš divas jaunas get_temperature()
un set_temperature()
metodes.
Turklāt temperature
tika aizstāts ar _temperature
. _
Sākumā pasvītrojums tiek izmantots, lai apzīmētu privātus mainīgos Python.
Tagad izmantosim šo ieviešanu:
# 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())
Rezultāts
37 98.60000000000001 Traceback (pēdējais zvans pēdējais): Fails "", 30. rindiņa, failā "", 16. rindiņa, set_temperature ValueError: Temperatūra zem -273.15 nav iespējama.
Šis atjauninājums veiksmīgi ieviesa jauno ierobežojumu. Mums vairs nav atļauts iestatīt temperatūru zem -273,15 grādiem pēc Celsija.
Piezīme . Privātie mainīgie Python faktiski nepastāv. Vienkārši ir jāievēro normas. Valoda pati par sevi nepiemēro nekādus ierobežojumus.
>>> human._temperature = -300 >>> human.get_temperature() -300
Tomēr lielāka problēma ar minēto atjauninājumu, ka visas programmas, kas ieviestas mūsu iepriekšējo klasi ir mainīt savu kodu obj.temperature
, lai obj.get_temperature()
un visas frāzes, piemēram, obj.temperature = val
uz obj.set_temperature(val)
.
Šī pārveidošana var radīt problēmas, strādājot ar simtiem tūkstošu kodu rindu.
Kopumā mūsu jaunais atjauninājums nebija savietojams atpakaļ. Šeit @property
nāk glābšana.
Īpašuma klase
Pitonisks veids, kā tikt galā ar iepriekš minēto problēmu, ir property
klases lietošana. Lūk, kā mēs varam atjaunināt savu kodu:
# 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)
Mēs pievienojām print()
funkciju iekšpusē get_temperature()
un set_temperature()
skaidri redzam, ka tie tiek izpildīti.
Pēdējā koda rindiņa veido rekvizīta objektu temperature
. Vienkārši sakot, īpašums pievieno kādu kodu ( get_temperature
un set_temperature
) dalībnieka atribūta piekļuvēm ( temperature
).
Izmantosim šo atjaunināšanas kodu:
# 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
Rezultāts
Vērtības iestatīšana … Vērtības iegūšana … 37 Vērtības iegūšana … 98.6000000000000001 Vērtības iestatīšana … Traceback (pēdējais zvans pēdējais): Fails "", 31. rindiņa failā "", 18. rindiņa, set_temperature Value Kļūda: Temperatūra zem -273 nav iespējama
Kā redzam, jebkurš kods, kas iegūst vērtību, temperature
automātiski tiks get_temperature()
izsaukts vārdnīcas (__dict__) uzmeklēšanas vietā. Līdzīgi temperature
automātiski tiks izsaukts jebkurš kods, kuram tiek piešķirta vērtība set_temperature()
.
Mēs pat iepriekš varam redzēt, ka set_temperature()
to sauca pat tad, kad mēs izveidojām objektu.
>>> human = Celsius(37) Setting value…
Vai jūs varat uzminēt, kāpēc?
Iemesls ir tāds, ka, izveidojot objektu, __init__()
metode tiek izsaukta. Šai metodei ir līnija self.temperature = temperature
. Šī izteiksme tiek automātiski izsaukta set_temperature()
.
Līdzīgi jebkura piekļuve, piemēram, c.temperature
automātiski zvani get_temperature()
. To dara īpašums. Šeit ir vēl daži piemēri.
>>> human.temperature Getting value 37 >>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001
Izmantojot property
, mēs varam redzēt, ka vērtības ierobežojuma ieviešanā nav jāveic nekādas izmaiņas. Tādējādi mūsu ieviešana ir savietojama atpakaļ.
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)
Šie divi kodi ir līdzvērtīgi.
Programmētāji, kas pazīstami ar Python dekoratoriem, var atpazīt, ka iepriekš minēto konstrukciju var īstenot kā dekorētājus.
Mēs pat nevaram definēt nosaukumus get_temperature
un, tā set_temperature
kā tie nav vajadzīgi, un piesārņot klases nosaukumvietu.
Šim nolūkam mēs atkārtoti izmantojam temperature
nosaukumu, vienlaikus definējot mūsu getter un setter funkcijas. Apskatīsim, kā to īstenot kā dekoratoru:
# 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)
Rezultāts
Vērtības iestatīšana … Vērtības iegūšana … 37 Vērtības iegūšana … 98.6000000000000001 Vērtības iestatīšana … Traceback (pēdējais zvans pēdējais): Fails "", 29. rindiņa failā "", 4. rindiņa, __init__ Fails "", 18. rindiņa, temperatūrā ValueError: Temperatūra zem -273 nav iespējama
Iepriekš minētā ieviešana ir vienkārša un efektīva. Tas ir ieteicamais lietošanas veids property
.