Python @property: Kako ga uporabljati in zakaj? - Programiz

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 @propertydekorater, ki olajša uporabo getterja in nastaviteljev pri objektno usmerjenem programiranju.

Preden se poglobimo v podrobnosti o tem, kaj je @propertydekorater, 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 temperatureatributi 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, temperaturekot je prikazano zgoraj, ga Python poišče v vgrajenem __dict__atributu slovarja predmeta .

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

Zato man.temperaturenotranje 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 temperaturebil 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.temperatureza obj.get_temperature()in vse izraze, kot obj.temperature = valda 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 @propertypride na pomoč.

Razred lastnosti

Pitonski način za reševanje zgornje težave je uporaba propertyrazreda. 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_temperaturein 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, temperaturesamodejno poklicala get_temperature()namesto iskanja po slovarju (__dict__). Podobno temperaturebo 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.temperaturesamodejni 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 propertylahko 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 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)

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_temperaturein set_temperature, kot so nepotrebni, in onesnažujejo razred imenskega prostora.

V ta namen ponovno uporabimo temperatureime, 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.

Zanimive Članki...