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ý @property
dekorá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 @property
je 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 temperature
atribú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 temperature
je uvedené vyššie, Python ho prehľadá v __dict__
atribúte vstavaného slovníka objektu.
>>> human.__dict__ ('temperature': 37)
Preto sa man.temperature
vnú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 temperature
bol 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.temperature
k obj.get_temperature()
a všetky výrazy ako obj.temperature = val
pre 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 @property
prichádza na záchranu.
Vlastnosť Class
Pytónovým spôsobom riešenia vyššie uvedeného problému je použitie property
triedy. 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_temperature
a 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, temperature
automaticky zavolá get_temperature()
namiesto vyhľadávania slovníka (__dict__). Podobne temperature
sa 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.temperature
automatické 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 property
vidí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 attributefset
is function to set value of the attributefdel
is function to delete the attributedoc
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_temperature
a set_temperature
pretože sú zbytočné a znečisťujú menný priestor.
Z tohto dôvodu opätovne používame temperature
ná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
.