Python @property: Hur man använder det och varför? - Programiz

I denna handledning lär du dig om Python @property decorator; ett pythoniskt sätt att använda getters och setter i objektorienterad programmering.

Python-programmering ger oss en inbyggd @propertydekoratör som gör användningen av getter och setter mycket enklare i Objektorienterad programmering.

Innan vi går in på detaljer om vad @propertydekoratör är, låt oss först bygga en intuition på varför det skulle behövas i första hand.

Klass utan getter och setter

Låt oss anta att vi bestämmer oss för att skapa en klass som lagrar temperaturen i grader Celsius. Det skulle också implementera en metod för att omvandla temperaturen till grader Fahrenheit. Ett sätt att göra detta är följande:

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

Vi kan göra objekt ur denna klass och manipulera temperatureattributet som vi vill:

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

Produktion

 37 98,60000000000001

De extra decimalerna vid konvertering till Fahrenheit beror på det flytande punkts aritmetiska felet. För att lära dig mer, besök Python Floating Point Arithmetic Error.

När vi tilldelar eller hämtar något objektattribut temperaturesom visas ovan, söker Python det i objektets inbyggda __dict__ordboksattribut.

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

Därför blir man.temperatureinternt man.__dict__('temperature').

Använda Getters och Setters

Antag att vi vill utöka användbarheten för Celsius-klassen som definierats ovan. Vi vet att temperaturen på något föremål inte kan understiga -273,15 grader Celsius (absolut noll inom termodynamik)

Låt oss uppdatera vår kod för att implementera denna värdebegränsning.

En uppenbar lösning på ovanstående begränsning är att dölja attributet temperature(göra det privat) och definiera nya getter- och settermetoder för att manipulera det. Detta kan göras enligt följande:

 # 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 introducerar ovanstående metod två nya get_temperature()och set_temperature()metoder.

Dessutom temperatureersattes med _temperature. En understrykning _i början används för att beteckna privata variabler i Python.

Låt oss nu använda den här 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())

Produktion

 37 98.60000000000001 Spårning (senaste samtalet senast): Fil "", rad 30, i fil "", rad 16, i set_temperature ValueError: Temperatur under -273.15 är inte möjlig.

Den här uppdateringen genomförde framgångsrikt den nya begränsningen. Vi får inte längre ställa in temperaturen under -273,15 grader Celsius.

Obs! De privata variablerna finns inte i Python. Det finns helt enkelt normer att följa. Själva språket tillämpar inga begränsningar.

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

Dock är större problem med ovanstående uppdatering som alla program som genomförs vår tidigare klass måste ändra sin kod från obj.temperatureatt obj.get_temperature()och alla uttryck som obj.temperature = valtill obj.set_temperature(val).

Denna refactoring kan orsaka problem när du hanterar hundratusentals rader med koder.

Sammantaget var vår nya uppdatering inte bakåtkompatibel. Det är här som @propertykommer att rädda.

Fastighetsklass

Ett pythoniskt sätt att hantera ovanstående problem är att använda propertyklassen. Så här kan vi uppdatera vår 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)

Vi lade till en print()funktion inuti get_temperature()och set_temperature()att tydligt observera att de körs.

Den sista raden i koden skapar ett fastighetsobjekt temperature. Enkelt uttryckt fäster egenskapen lite kod ( get_temperatureoch set_temperature) till medlemsattributåtgångarna ( temperature).

Låt oss använda den här uppdateringskoden:

 # 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

Produktion

 Inställningsvärde … Få värde … 37 Få värde … 98.60000000000001 Inställningsvärde … Spårning (senaste samtalet senast): Fil "", rad 31, i fil "", rad 18, i set_temperature ValueError: Temperatur under -273 är inte möjlig

Som vi kan se kommer alla koder som hämtar värdet av temperatureautomatiskt att ringa get_temperature()istället för en ordlista (__dict__). På samma sätt kommer alla koder som tilldelar ett värde att temperatureringa automatiskt set_temperature().

Vi kan till och med se ovan som set_temperature()kallades även när vi skapade ett objekt.

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

Kan du gissa varför?

Anledningen är att när ett objekt skapas __init__()kallas metoden. Denna metod har linjen self.temperature = temperature. Detta uttryck ringer automatiskt set_temperature().

På samma sätt, all åtkomst som c.temperatureautomatiskt samtal get_temperature(). Detta är vad egendom gör. Här är några fler exempel.

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

Genom att använda propertykan vi se att ingen ändring krävs i implementeringen av värdebegränsningen. Således är vår implementering bakåtkompatibel.

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)

Dessa två koder är likvärdiga.

Programmerare som är bekanta med Python Decorators kan inse att konstruktionen ovan kan implementeras som dekoratörer.

Vi kan inte ens definiera namnen get_temperatureoch set_temperatureeftersom de är onödiga och förorenar klassnamnområdet.

För detta återanvänder vi temperaturenamnet medan vi definierar våra getter- och setterfunktioner. Låt oss titta på hur man implementerar detta 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)

Produktion

 Inställningsvärde … Få värde … 37 Få värde … 98.60000000000001 Inställningsvärde … Spårning (senaste samtalet senast): Fil "", rad 29, i fil "", rad 4, i __init__ fil "", rad 18, i temperatur ValueError: Temperatur under -273 är inte möjlig

Ovanstående implementering är enkel och effektiv. Det är det rekommenderade sättet att använda property.

Intressanta artiklar...