V tej vadnici boste izvedeli več o okrasitelju Python @property; pitoničen način uporabe getterjev in setterjev v objektno usmerjenem programiranju.
Programiranje na Python nam ponuja vgrajen @property
dekorater, ki olajša uporabo getterja in nastaviteljev pri objektno usmerjenem programiranju.
Preden se poglobimo v podrobnosti o tem, kaj je @property
dekorater, si najprej izdelajmo intuicijo, zakaj bi bil sploh potreben.
Razred brez pridobivalcev in seterjev
Predpostavimo, da smo se odločili za razred, ki shranjuje temperaturo v stopinjah Celzija. Uvedel bi tudi metodo za pretvorbo temperature v stopinje Fahrenheita. Eden od načinov za to je naslednji:
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32
Iz tega razreda lahko izdelamo predmete in z temperature
atributi upravljamo, kot želimo:
# 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())
Izhod
37 98,60000000000001
Dodatna decimalna mesta pri pretvorbi v Fahrenheit so posledica aritmetične napake s plavajočo vejico. Če želite izvedeti več, obiščite Python Arithmetic Error s plavajočo vejico.
Kadarkoli dodelimo ali pridobimo kateri koli atribut predmeta, temperature
kot je prikazano zgoraj, ga Python poišče v vgrajenem __dict__
atributu slovarja predmeta .
>>> human.__dict__ ('temperature': 37)
Zato man.temperature
notranje postane man.__dict__('temperature')
.
Uporaba Getterjev in Setterjev
Recimo, da želimo razširiti uporabnost zgoraj definiranega razreda Celzija. Vemo, da temperatura katerega koli predmeta ne more doseči pod -273,15 stopinj Celzija (Absolutna ničla v termodinamiki)
Posodobimo svojo kodo, da bomo izvedli to omejitev vrednosti.
Očitna rešitev zgornje omejitve bo skriti atribut temperature
(narediti ga zasebnim) in določiti nove metode pridobivanja in nastavitve za njegovo manipulacijo. To lahko storite na naslednji način:
# 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
Kot lahko vidimo, zgornja metoda uvaja dve novi get_temperature()
in set_temperature()
metodi.
Nadalje je temperature
bil nadomeščen z _temperature
. Podčrtaj _
na začetku se uporablja za označevanje zasebnih spremenljivk v Pythonu.
Zdaj pa uporabimo to izvedbo:
# 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())
Izhod
37 98.60000000000001 Sledenje (zadnji zadnji klic): Datoteka "", vrstica 30, v datoteki "", vrstica 16, v nastavljeni_temperaturi ValueError: Temperatura pod -273,15 ni možna.
Ta posodobitev je uspešno izvedla novo omejitev. Ne smemo več nastavljati temperature pod -273,15 stopinj Celzija.
Opomba : Zasebne spremenljivke v Pythonu dejansko ne obstajajo. Preprosto obstajajo norme, ki jih je treba upoštevati. Jezik sam ne uporablja nobenih omejitev.
>>> human._temperature = -300 >>> human.get_temperature() -300
Vendar pa je večji problem z zgornjo posodobitev je, da imajo vsi programi, ki se izvajajo našo prejšnjo razred spremeniti svojo kodo obj.temperature
za obj.get_temperature()
in vse izraze, kot obj.temperature = val
da obj.set_temperature(val)
.
Ta refaktoring lahko povzroči težave pri obravnavi stotisoč vrstic kod.
Vsekakor naša nova posodobitev ni bila združljiva nazaj. Tu @property
pride na pomoč.
Razred lastnosti
Pitonski način za reševanje zgornje težave je uporaba property
razreda. Evo, kako lahko posodobimo svojo kodo:
# 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)
V print()
notranjost smo dodali funkcijo get_temperature()
in set_temperature()
jasno opazili, da se izvajajo.
Zadnja vrstica kode naredi objekt lastnosti temperature
. Preprosto povedano, lastnost pripiše nekaj kode ( get_temperature
in set_temperature
) dostopom do atributa člana ( temperature
).
Uporabimo to kodo posodobitve:
# 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
Izhod
Nastavitev vrednosti… Pridobivanje vrednosti… 37 Pridobivanje vrednosti… 98.60000000000001 Nastavitev vrednosti… Traceback (zadnji zadnji klic): Datoteka »«, vrstica 31, v datoteki »«, vrstica 18, v set_temperature ValueError: Temperatura pod -273 ni možna
Kot lahko vidimo, bo vsaka koda, ki pridobi vrednost, temperature
samodejno poklicala get_temperature()
namesto iskanja po slovarju (__dict__). Podobno temperature
bo samodejno poklicala vsaka koda, ki ji dodeli vrednost set_temperature()
.
Zgoraj lahko vidimo tudi, kako se set_temperature()
je imenovalo, tudi ko smo ustvarili predmet.
>>> human = Celsius(37) Setting value…
Ali lahko ugibate zakaj?
Razlog je v tem, da se pri ustvarjanju predmeta __init__()
metoda pokliče. Ta metoda ima črto self.temperature = temperature
. Ta izraz samodejno pokliče set_temperature()
.
Podobno je vsak dostop, kot so c.temperature
samodejni klici get_temperature()
. To počne lastništvo. Tu je še nekaj primerov.
>>> human.temperature Getting value 37 >>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001
Z uporabo property
lahko vidimo, da pri izvedbi omejitve vrednosti ni potrebna nobena sprememba. Tako je naša izvedba združljiva z nazaj.
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)
Ta dva koda sta enakovredna.
Programerji, ki poznajo Python Decorators, lahko prepoznajo, da je zgornji konstrukt mogoče uporabiti kot dekoraterje.
Mi tudi ne moremo določiti imena get_temperature
in set_temperature
, kot so nepotrebni, in onesnažujejo razred imenskega prostora.
V ta namen ponovno uporabimo temperature
ime, medtem ko definiramo funkcije getter in setter. Poglejmo, kako to uporabiti kot dekorater:
# 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)
Izhod
Nastavitev vrednosti… Pridobivanje vrednosti… 37 Pridobivanje vrednosti… 98.60000000000001 Nastavitev vrednosti… Traceback (zadnji zadnji klic): Datoteka »«, vrstica 29, v datoteki »«, vrstica 4, v __init__ Datoteka »«, vrstica 18, pri temperaturi ValueError: Temperatura pod -273 ni mogoča
Zgornja izvedba je preprosta in učinkovita. To je priporočljiv način uporabe property
.