Python @property: Kā to izmantot un kāpēc? - Programiz

Š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 @propertydekoratoru, kas padara objektorientētā programmēšanā daudz vieglāku getter un seters lietošanu.

Pirms iedziļināties detaļās par to, kas ir @propertydekorators, 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 temperatureatribū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, temperaturekā 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.temperatureiekšē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 temperaturetika 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 = valuz 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 @propertynāk glābšana.

Īpašuma klase

Pitonisks veids, kā tikt galā ar iepriekš minēto problēmu, ir propertyklases 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_temperatureun 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, temperatureautomātiski tiks get_temperature()izsaukts vārdnīcas (__dict__) uzmeklēšanas vietā. Līdzīgi temperatureautomā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.temperatureautomā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 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)

Š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_temperatureun, tā set_temperaturekā tie nav vajadzīgi, un piesārņot klases nosaukumvietu.

Šim nolūkam mēs atkārtoti izmantojam temperaturenosaukumu, 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.

Interesanti raksti...