Python @property: Ako ho používať a prečo? - Programiz

V tomto výučbe sa dozviete o dekoratérii Python @property; pythonický spôsob použitia getrov a setterov v objektovo orientovanom programovaní.

Programovanie v jazyku Python nám poskytuje zabudovaný @propertydekorátor, ktorý výrazne uľahčuje používanie getra a setra v objektovo orientovanom programovaní.

Predtým, ako sa pustíme do podrobností o tom, čo @propertyje to dekoratér, najskôr si vytvorme intuíciu, prečo by to bolo vlastne potrebné.

Trieda bez getrov a setrov

Predpokladajme, že sa rozhodneme vytvoriť triedu, ktorá bude uchovávať teplotu v stupňoch Celzia. Tiež by sa implementovala metóda na prevod teploty na stupne Fahrenheita. Jeden spôsob, ako to dosiahnuť, je nasledujúci:

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

Z tejto triedy môžeme vytvárať objekty a manipulovať s temperatureatribútom, ako si prajeme:

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

Výkon

 37 98.60000000000001

Extra desatinné miesta pri prepočte na stupne Fahrenheita sú spôsobené aritmetickou chybou s pohyblivou desatinnou čiarkou. Ak sa chcete dozvedieť viac, navštívte aritmetickú chybu s pohyblivou rádovou čiarkou Pythonu.

Kedykoľvek priradíme alebo načítame ľubovoľný atribút objektu, ako temperatureje uvedené vyššie, Python ho prehľadá v __dict__atribúte vstavaného slovníka objektu.

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

Preto sa man.temperaturevnútorne stáva man.__dict__('temperature').

Používanie Getters a Setters

Predpokladajme, že chceme rozšíriť použiteľnosť triedy Celzia definovanej vyššie. Vieme, že teplota žiadneho objektu nemôže dosiahnuť pod -273,15 stupňov Celzia (absolútna nula v termodynamike)

Aktualizujeme náš kód, aby sme implementovali toto obmedzenie hodnoty.

Zrejmým riešením vyššie uvedeného obmedzenia bude skryť atribút temperature(urobiť ho súkromným) a definovať nové metódy getra a setra na manipuláciu s ním. Môžete to urobiť nasledovne:

 # 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

Ako vidíme, vyššie uvedená metóda zavádza dve nové get_temperature()a set_temperature()metódy.

Ďalej temperaturebol nahradený _temperature. Podčiarkovník _na začiatku sa používa na označenie súkromných premenných v Pythone.

Teraz použijeme túto implementáciu:

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

Výkon

 37 98.60000000000001 Traceback (posledné volanie posledné): File "", riadok 30, v File "", riadok 16, v set_temperature ValueError: Teplota pod -273,15 nie je možná.

Táto aktualizácia úspešne implementovala nové obmedzenie. Už nesmieme nastavovať teplotu pod -273,15 stupňov Celzia.

Poznámka : Súkromné ​​premenné v skutočnosti v Pythone neexistujú. Existujú jednoducho normy, ktoré treba dodržiavať. Samotný jazyk neuplatňuje žiadne obmedzenia.

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

Avšak, väčší problém s vyššie aktualizácie je, že všetky programy, ktoré zaviedli náš predchádzajúci triedu musieť zmeniť svoj kód z obj.temperaturek obj.get_temperature()a všetky výrazy ako obj.temperature = valpre obj.set_temperature(val).

Toto prefinancovanie môže spôsobiť problémy pri práci so státisícami riadkov kódov.

Naša nová aktualizácia nebola celkovo spätne kompatibilná. To je miesto, kde @propertyprichádza na záchranu.

Vlastnosť Class

Pytónovým spôsobom riešenia vyššie uvedeného problému je použitie propertytriedy. Náš kód môžeme aktualizovať takto:

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

Pridali sme print()funkciu dovnútra get_temperature()a set_temperature()aby sme jasne videli, že sa vykonávajú.

Posledný riadok kódu vytvára objekt vlastnosti temperature. Jednoducho povedané, vlastnosť pripája nejaký kód ( get_temperaturea set_temperature) k členskému atribútu accesses ( temperature).

Použime tento aktualizačný kód:

 # 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

Výkon

 Nastavená hodnota … Získava sa hodnota … 37 Získava sa hodnota … 98.60000000000001 Nastavená hodnota … Traceback (posledné volanie posledné): Súbor „“, riadok 31, v súbore „“, riadok 18, nastavená teplota ValueError: Teplota pod -273 nie je možná

Ako vidíme, akýkoľvek kód, ktorý získa hodnotu, temperatureautomaticky zavolá get_temperature()namiesto vyhľadávania slovníka (__dict__). Podobne temperaturesa automaticky zavolá akýkoľvek kód, ktorý priradí hodnotu set_temperature().

Dokonca môžeme vidieť vyššie, že set_temperature()sa to volalo, aj keď sme vytvorili objekt.

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

Uhádnete prečo?

Dôvod je ten, že pri vytvorení objektu sa __init__()metóda zavolá. Táto metóda má riadok self.temperature = temperature. Tento výraz automaticky zavolá set_temperature().

Podobne každý prístup, ako napríklad c.temperatureautomatické volanie get_temperature(). Toto robí vlastnosť. Tu uvádzam niekoľko ďalších príkladov.

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

Pomocou použitia propertyvidíme, že pri implementácii obmedzenia hodnoty nie sú potrebné žiadne úpravy. Naša implementácia je teda spätne kompatibilná.

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)

Tieto dva kódy sú rovnocenné.

Programátori oboznámení s dekorátormi Pythonu vedia, že vyššie uvedený konštrukt je možné implementovať ako dekoratéry.

Môžeme dokonca nedefinuje názvy get_temperaturea set_temperaturepretože sú zbytočné a znečisťujú menný priestor.

Z tohto dôvodu opätovne používame temperaturenázov a zároveň definujeme naše funkcie getra a setra. Pozrime sa, ako to implementovať ako 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)

Výkon

 Nastavená hodnota … Získava sa hodnota … 37 Získava sa hodnota … 98.60000000000001 Nastavená hodnota … Traceback (posledné volanie naposledy): Súbor „“, riadok 29, v súbore „“, riadok 4, v __init__ Súbor „“, riadok 18, teplota ValueError: Teplota pod -273 nie je možná

Vyššie uvedená implementácia je jednoduchá a efektívna. Je to odporúčaný spôsob použitia property.

Zaujímavé články...