„Python @property“: kaip jį naudoti ir kodėl? - Programiz

Šioje pamokoje sužinosite apie „Python @property“ dekoratorių; pitoninis būdas naudoti geterius ir seterius programuojant į objektą.

„Python“ programavimas suteikia mums įmontuotą @propertydekoratorių, kuris žymiai palengvina „getter“ ir „seters“ naudojimą programuojant į objektą.

Prieš gilindamiesi į detales, kas @propertyyra dekoratorius, pirmiausia sukurkime intuiciją, kodėl jo pirmiausia reikėtų.

Klasė be geterių ir seterių

Tarkime, kad nusprendėme sukurti klasę, kurioje temperatūra laikoma Celsijaus laipsniais. Taip pat būtų įgyvendintas metodas, kuriuo temperatūra būtų konvertuojama į Farenheito laipsnius. Vienas iš būdų tai padaryti yra toks:

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

Mes galime pagaminti objektus iš šios klasės ir manipuliuoti temperatureatributu, kaip norime:

 # 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())

Rezultatas

 37 98.60000000000001

Papildomi skaičiai po kablelio, perskaičiuojant į Farenheitą, yra dėl slankiojo kablelio aritmetinės klaidos. Norėdami sužinoti daugiau, apsilankykite „Python“ slankiojo kablelio aritmetinėje klaidoje.

Kai priskiriame ar gauname bet kokį objekto atributą, temperaturekaip parodyta aukščiau, „Python“ ieško jo objekto vidiniame __dict__žodyno atribute.

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

Todėl man.temperatureviduje tampa man.__dict__('temperature').

Naudojant „Getters“ ir „Setters“

Tarkime, kad norime išplėsti aukščiau apibrėžtos Celsijaus klasės naudingumą. Mes žinome, kad bet kurio objekto temperatūra negali būti žemesnė nei –273,15 laipsnių Celsijaus (absoliutus nulis termodinamikoje)

Atnaujinkime savo kodą, kad įgyvendintume šį vertės apribojimą.

Akivaizdus pirmiau minėto apribojimo sprendimas bus atributo paslėpimas temperature(paversti jį privačiu) ir nustatyti naujus „Getter“ ir „Setter“ metodus, kaip juo manipuliuoti. Tai galima padaryti taip:

 # 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

Kaip matome, pirmiau metodas pristato du naujus get_temperature()ir set_temperature()metodus.

Be to, temperaturebuvo pakeistas į _temperature. _Pradžioje esantis pabraukimas naudojamas norint žymėti privačius „Python“ kintamuosius.

Dabar naudokime šį įgyvendinimą:

 # 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())

Rezultatas

 37 98.60000000000001 „Traceback“ (paskutinis paskutinis skambutis paskutinis): Failas „“, 30 eilutė, faile „“, 16 eilutė, set_temperature ValueError: temperatūra žemesnė nei -273.15 neįmanoma.

Šis atnaujinimas sėkmingai įdiegė naują apribojimą. Mums nebeleidžiama nustatyti temperatūros žemiau –273,15 laipsnių Celsijaus.

Pastaba : privačių kintamųjų „Python“ iš tikrųjų nėra. Paprasčiausiai yra normų, kurių reikia laikytis. Pati kalba netaiko jokių apribojimų.

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

Tačiau didesnė problema su aukščiau atnaujinimo yra tai, kad visos programos, kad įgyvendintų savo ankstesnę klasę turi keisti savo kodą iš obj.temperatureį obj.get_temperature()ir visus išraiškas kaip obj.temperature = valį obj.set_temperature(val).

Šis pertvarkymas gali sukelti problemų tvarkant šimtus tūkstančių kodų eilučių.

Apskritai, mūsų naujinys nebuvo suderinamas atgaline data. Čia @propertyateina gelbėti.

Turto klasė

Pitoniškas būdas išspręsti minėtą problemą yra propertyklasės naudojimas. Štai kaip galime atnaujinti savo 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)

Pridėjome print()funkciją viduje get_temperature()ir set_temperature()aiškiai pastebėjome, kad jos vykdomos.

Paskutinė kodo eilutė sudaro nuosavybės objektą temperature. Paprasčiau tariant, nuosavybė prideda tam tikrą kodą ( get_temperatureir set_temperature) prie nario atributo prieigų ( temperature).

Panaudokime šį naujinimo 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) human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) human.temperature = -300

Rezultatas

 Nustatoma vertė … Gaunama vertė … 37 Gaunama vertė … 98.6000000000000001 Nustatoma vertė … „Traceback“ (paskutinis skambutis paskutinis): failas „“, 31 eilutė faile „“, 18 eilutė, „set_temperature“ reikšmė Klaida: Žemesnė nei -273 temperatūra neįmanoma

Kaip matome, bet koks kodas, gaunantis reikšmę, temperatureautomatiškai skambins get_temperature()vietoj žodyno (__dict__) paieškos. Panašiai bet kuris kodas, kuriam priskiriama reikšmė, temperatureautomatiškai skambins set_temperature().

Mes netgi galime pamatyti aukščiau, kuris set_temperature()buvo vadinamas net tada, kai sukūrėme objektą.

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

Ar galite atspėti, kodėl?

Priežastis ta, kad kai sukuriamas objektas, __init__()metodas iškviečiamas. Šis metodas turi liniją self.temperature = temperature. Ši išraiška automatiškai skambina set_temperature().

Be to, bet kokia prieiga, pvz., c.temperatureAutomatiškai skambinama get_temperature(). Tai daro nuosavybė. Čia yra dar keli pavyzdžiai.

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

Naudodami propertymatome, kad nereikia keisti jokių vertės apribojimų. Taigi mūsų įgyvendinimas yra suderinamas atgal.

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 du kodai yra lygiaverčiai.

Programuotojai, susipažinę su „Python“ dekoratoriais, gali atpažinti, kad aukščiau pateiktą konstrukciją galima įgyvendinti kaip dekoratorius.

Mes net negalime apibrėžti pavadinimų get_temperature, set_temperaturenes jie nereikalingi, ir užteršti klasės vardų sritį.

Tam mes pakartotinai naudojame temperaturepavadinimą, apibrėždami savo „Getter“ ir „Setter“ funkcijas. Pažvelkime, kaip tai įgyvendinti kaip dekoratorių:

 # 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)

Rezultatas

 Nustatoma reikšmė … Gaunama vertė … 37 Gaunama vertė … 98.6000000000000001 Nustatoma reikšmė … „Traceback“ (paskutinis skambutis paskutinis): Failas „“, 29 eilutė faile „“, 4 eilutė, __init__ Failas „“, 18 eilutė, temperatūroje „ValueError“: Žemesnė nei -273 temperatūra neįmanoma

Pirmiau pateiktas įgyvendinimas yra paprastas ir efektyvus. Tai yra rekomenduojamas naudojimo būdas property.

Įdomios straipsniai...