Python @property: hoe te gebruiken en waarom? - Programiz

In deze tutorial leer je over Python @property decorator; een pythonische manier om getters en setters te gebruiken bij objectgeoriënteerd programmeren.

Python-programmering biedt ons een ingebouwde @propertydecorateur die het gebruik van getter en setters veel gemakkelijker maakt in Object-Oriented Programming.

Voordat we ingaan op wat @propertydecorateur is, laten we eerst een intuïtie opbouwen over waarom het in de eerste plaats nodig zou zijn.

Klasse zonder getters en setters

Laten we aannemen dat we besluiten een klasse te maken die de temperatuur opslaat in graden Celsius. Het zou ook een methode implementeren om de temperatuur om te rekenen naar graden Fahrenheit. Een manier om dit te doen is als volgt:

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

We kunnen objecten uit deze klasse maken en het temperatureattribuut naar wens manipuleren :

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

Uitvoer

 37 98.60000000000001

De extra decimalen bij het converteren naar Fahrenheit zijn te wijten aan de rekenfout met drijvende komma. Ga voor meer informatie naar Python Floating Point Arithmetic Error.

Telkens wanneer we een objectkenmerk toewijzen of ophalen zoals temperaturehierboven weergegeven, zoekt Python het in het ingebouwde __dict__woordenboekkenmerk van het object .

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

Daarom wordt man.temperatureintern man.__dict__('temperature').

Getters en Setters gebruiken

Stel dat we de bruikbaarheid van de hierboven gedefinieerde Celsius-klasse willen uitbreiden. We weten dat de temperatuur van een object niet onder de -273,15 graden Celsius kan komen (het absolute nulpunt in de thermodynamica)

Laten we onze code bijwerken om deze waardebeperking te implementeren.

Een voor de hand liggende oplossing voor de bovenstaande beperking is om het attribuut te verbergen temperature(privé te maken) en nieuwe getter- en setter-methoden te definiëren om het te manipuleren. Dit kan als volgt worden gedaan:

 # 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

Zoals we kunnen zien, de bovenstaande methode introduceert twee nieuwe get_temperature()en set_temperature()methoden.

Verder temperaturewerd vervangen door _temperature. Een onderstrepingsteken _aan het begin wordt gebruikt om privévariabelen in Python aan te duiden.

Laten we nu deze implementatie gebruiken:

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

Uitvoer

 37 98.60000000000001 Traceback (meest recente oproep laatste): Bestand "", regel 30, in Bestand "", regel 16, in set_temperature ValueError: Temperatuur onder -273.15 is niet mogelijk.

Deze update heeft de nieuwe beperking geïmplementeerd. We mogen de temperatuur niet meer onder de -273,15 graden Celsius instellen.

Opmerking : de privévariabelen bestaan ​​niet echt in Python. Er zijn gewoon normen die moeten worden gevolgd. De taal zelf kent geen beperkingen.

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

Het grotere probleem met de bovenstaande update is echter dat alle programma's die onze vorige klasse hebben geïmplementeerd hun code moeten wijzigen van obj.temperaturenaar obj.get_temperature()en alle uitdrukkingen willen obj.temperature = valdat obj.set_temperature(val).

Deze herstructurering kan problemen veroorzaken bij het omgaan met honderdduizenden regels codes.

Al met al was onze nieuwe update niet achterwaarts compatibel. Dit is waar @propertyte hulp komt.

De eigenschap Class

Een pythonische manier om met het bovenstaande probleem om te gaan, is door de propertyklasse te gebruiken . Hier is hoe we onze code kunnen updaten:

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

We voegden een print()functie binnen get_temperature()en set_temperature()om duidelijk te zien dat ze worden uitgevoerd.

De laatste regel van de code maakt een eigenschapsobject temperature. Simpel gezegd, eigenschap voegt een code ( get_temperatureen set_temperature) toe aan het lidattribuut accesses ( temperature).

Laten we deze updatecode gebruiken:

 # 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

Uitvoer

 Waarde instellen … Waarde ophalen … 37 Waarde ophalen … 98.60000000000001 Waarde instellen … Traceback (laatste oproep laatste): Bestand "", regel 31, in Bestand "", regel 18, in set_temperature ValueError: Temperatuur onder -273 is niet mogelijk

As we can see, any code that retrieves the value of temperature will automatically call get_temperature() instead of a dictionary (__dict__) look-up. Similarly, any code that assigns a value to temperature will automatically call set_temperature().

We can even see above that set_temperature() was called even when we created an object.

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

Can you guess why?

The reason is that when an object is created, the __init__() method gets called. This method has the line self.temperature = temperature. This expression automatically calls set_temperature().

Similarly, any access like c.temperature automatically calls get_temperature(). This is what property does. Here are a few more examples.

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

By using property, we can see that no modification is required in the implementation of the value constraint. Thus, our implementation is backward compatible.

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)

Deze twee stukjes codes zijn equivalent.

Programmeurs die bekend zijn met Python Decorators kunnen erkennen dat het bovenstaande construct kan worden geïmplementeerd als decorateurs.

We kunnen zelfs de namen niet definiëren get_temperatureen set_temperatureomdat ze niet nodig zijn en de naamruimte van de klassen vervuilen.

Hiervoor gebruiken we de temperaturenaam opnieuw terwijl we onze getter- en setter-functies definiëren. Laten we eens kijken hoe we dit als decorateur kunnen implementeren:

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

Uitvoer

 Waarde instellen … Waarde ophalen … 37 Waarde ophalen … 98.60000000000001 Waarde instellen … Traceback (meest recente oproep laatste): Bestand "", regel 29, in Bestand "", regel 4, in __init__ Bestand "", regel 18, in temperatuur ValueError: Temperatuur onder -273 is niet mogelijk

De bovenstaande implementatie is eenvoudig en efficiënt. Het is de aanbevolen manier om te gebruiken property.

Interessante artikelen...