Python @property: Hvordan bruke den og hvorfor? - Programiz

I denne veiledningen vil du lære om Python @property decorator; en pytonisk måte å bruke getters og setter i objektorientert programmering.

Python-programmering gir oss en innebygd @propertydekoratør som gjør bruk av getter og setter mye lettere i Objektorientert programmering.

Før vi går inn i detaljer om hva @propertydekoratør er, la oss først bygge en intuisjon på hvorfor det ville være nødvendig i utgangspunktet.

Klasse Uten Getters og Setters

La oss anta at vi bestemmer oss for å lage en klasse som lagrer temperaturen i grader Celsius. Det ville også implementere en metode for å konvertere temperaturen til grader Fahrenheit. En måte å gjøre dette på er som følger:

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

Vi kan lage objekter ut av denne klassen og manipulere temperatureattributtet slik vi ønsker:

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

Produksjon

 37 98,60000000000001

De ekstra desimalene ved konvertering til Fahrenheit skyldes den flytende punktets aritmetiske feil. For å lære mer, besøk Python Floating Point Arithmetic Error.

Når vi tilordner eller henter et objektattributt temperaturesom vist ovenfor, søker Python det i objektets innebygde __dict__ordbokattributt.

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

Derfor blir man.temperatureinternt man.__dict__('temperature').

Bruke Getters og Setters

Anta at vi vil utvide brukervennligheten til Celsius-klassen definert ovenfor. Vi vet at temperaturen på ethvert objekt ikke kan nå under -273,15 grader Celsius (Absolute Zero in Thermodynamics)

La oss oppdatere koden vår for å implementere denne verdibegrensningen.

En åpenbar løsning på ovennevnte begrensning vil være å skjule attributtet temperature(gjøre det privat) og definere nye getter- og settermetoder for å manipulere det. Dette kan gjøres som følger:

 # 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

Som vi kan se, introduserer metoden ovenfor to nye get_temperature()og set_temperature()metoder.

Videre temperatureble erstattet med _temperature. Et understrek _i begynnelsen brukes til å betegne private variabler i Python.

La oss nå bruke denne implementeringen:

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

Produksjon

 37 98.60000000000001 Sporing (siste anrop sist): Fil "", linje 30, i fil "", linje 16, i set_temperature Verdifeil: Temperatur under -273.15 er ikke mulig.

Denne oppdateringen implementerte den nye begrensningen. Vi har ikke lenger lov til å sette temperaturen under -273,15 grader Celsius.

Merk : De private variablene eksisterer faktisk ikke i Python. Det er rett og slett normer å følge. Språket i seg selv bruker ikke noen begrensninger.

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

Men større problem med ovennevnte oppdateringen er at alle programmene som er implementert vår forrige klassen må endre sin kode fra obj.temperatureå obj.get_temperature()og alle uttrykk som obj.temperature = valtil obj.set_temperature(val).

Denne refactoring kan forårsake problemer når du arbeider med hundretusener av linjer med koder.

Alt i alt var den nye oppdateringen vår ikke bakoverkompatibel. Dette er hvor @propertykommer for å redde.

Eiendomsklassen

En pytonisk måte å håndtere problemet ovenfor er å bruke propertyklassen. Slik kan vi oppdatere koden vår:

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

Vi la til en print()funksjon inne get_temperature()og for set_temperature()å tydelig observere at de blir utført.

Den siste linjen i koden utgjør et eiendomsobjekt temperature. Enkelt sagt, eiendom fester noe kode ( get_temperatureog set_temperature) til medlemsattributttilgangene ( temperature).

La oss bruke denne oppdateringskoden:

 # 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

Produksjon

 Innstillingsverdi … Få verdi … 37 Få verdi … 98.60000000000001 Innstillingsverdi … Sporing (siste anrop sist): Fil "", linje 31, i fil "", linje 18, i set_temperature ValueError: Temperatur under -273 er ​​ikke mulig

Som vi kan se, vil en hvilken som helst kode som henter verdien av temperature, automatisk ringe opp i get_temperature()stedet for en ordbok (__dict__). På samme måte vil enhver kode som tildeler en verdi temperatureautomatisk ringe set_temperature().

Vi kan til og med se ovenfor som set_temperature()ble kalt selv når vi opprettet et objekt.

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

Kan du gjette hvorfor?

Årsaken er at når et objekt blir opprettet, blir __init__()metoden kalt. Denne metoden har linjen self.temperature = temperature. Dette uttrykket ringer automatisk set_temperature().

Tilsvarende tilgang som c.temperatureautomatisk samtaler get_temperature(). Dette er hva eiendommen gjør. Her er noen flere eksempler.

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

Ved å bruke propertykan vi se at ingen endringer er nødvendige i implementeringen av verdibegrensningen. Dermed er implementeringen vår bakoverkompatibel.

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)

Disse to kodene er ekvivalente.

Programmerere som er kjent med Python Decorators, kan erkjenne at konstruksjonen ovenfor kan implementeres som dekoratører.

Vi kan til og med ikke definere navnene, get_temperatureog set_temperaturefordi de er unødvendige og forurenser klassens navneområde.

For dette gjenbruker vi temperaturenavnet mens vi definerer våre getter- og setterfunksjoner. La oss se på hvordan du implementerer dette som dekoratør:

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

Produksjon

 Innstillingsverdi … Få verdi … 37 Få verdi … 98.60000000000001 Innstillingsverdi … Spor tilbake (siste samtale sist): Fil "", linje 29, i fil "", linje 4, i __init__ fil "", linje 18, i temperatur Verdifeil: Temperatur under -273 er ​​ikke mulig

Ovennevnte implementering er enkel og effektiv. Det er den anbefalte måten å bruke property.

Interessante artikler...