Šioje pamokoje sužinosite apie „Python @property“ dekoratorių; pitoninis būdas naudoti geterius ir seterius programuojant į objektą.
„Python“ programavimas suteikia mums įmontuotą @property
dekoratorių, kuris žymiai palengvina „getter“ ir „seters“ naudojimą programuojant į objektą.
Prieš gilindamiesi į detales, kas @property
yra 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 temperature
atributu, 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ą, temperature
kaip parodyta aukščiau, „Python“ ieško jo objekto vidiniame __dict__
žodyno atribute.
>>> human.__dict__ ('temperature': 37)
Todėl man.temperature
viduje 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, temperature
buvo 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 @property
ateina gelbėti.
Turto klasė
Pitoniškas būdas išspręsti minėtą problemą yra property
klasė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_temperature
ir 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ę, temperature
automatiškai skambins get_temperature()
vietoj žodyno (__dict__) paieškos. Panašiai bet kuris kodas, kuriam priskiriama reikšmė, temperature
automatiš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.temperature
Automatiš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 property
matome, 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 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 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_temperature
nes jie nereikalingi, ir užteršti klasės vardų sritį.
Tam mes pakartotinai naudojame temperature
pavadinimą, 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
.