18. diel - Vlastnosti v Pythone
V predchádzajúcej lekcii, Dekorátory druhýkrát - Parametrické a triedne dekorátory , sme dokončili tému s dekorátormi.
V dnešnom tutoriále objektovo orientovaného programovania v Pythone sa pozrieme na ďalšie prvky tried, ktoré ešte nepoznáme. Zameriame sa na vlastnosti (properties). Ukážeme si, ako vďaka nim elegantne vyriešime nastavovanie a validáciu hodnôt atribútov.
Vlastnosti v Pythone
Vlastnosti nám ponúkajú elegantný a flexibilný spôsob, ako pracovať s atribútmi objektov. Vo svojej podstate nám umožňujú definovať metódy, ktoré vyzerajú a chovajú sa ako atribúty. Vďaka tomu máme pod kontrolou, čo sa stane, keď je atribút nastavený, čítaný, alebo dokonca mazaný, a to všetko s uchovaním rovnakého rozhrania ako pri obyčajných atribútoch. To je užitočné v kontexte zapuzdrenia dát a obmedzenia priameho prístupu k dátam objektu, hoci rozhodne nejde o absolútne riešenie. V mnohých iných jazykoch je zapuzdrenie implementované pomocou súkromných atribútov a verejných metód (getters a setters). My už tušíme, že Python má ako obvykle svoj vlastný špecifický prístup. Ponúka nám vlastnosti, ktoré tieto koncepty spájajú do jedného elegantného a čitateľného riešenia.
Dekorátor @property
Kľúčovým prvkom pre vytvorenie vlastnosti je dekorátor
@property
. Ak máme jednoduchý atribút, ktorý chceme upraviť
tak, aby obsahoval validáciu alebo dodatočnú logiku pri čítaní, práve
týmto dekorátorom ho premeníme na vlastnosť.
Majme napríklad triedu Osoba
s atribútom vek
.
Chceme zaistiť, že vek bude vždy kladné číslo. Preto tento atribút
premeníme na vlastnosť a pridáme zodpovedajúcu validáciu:
class Osoba:
def __init__(self, vek):
self._vek = vek # soukromý atribut vek
@property #
def vek(self):
return f"Věk osoby je {self._vek} let."
@vek.setter
def vek(self, hodnota):
if hodnota < 0:
print("Chyba! Věk nemůže být záporný")
else:
self._vek = hodnota
clovek = Osoba(10)
clovek.vek = -20
print(clovek.vek)
V tomto príklade je _vek
skrytý atribút, ktorý skutočne
uchováva hodnotu veku. Verejná vlastnosť vek
(už bez
podčiarkovníka) potom umožňuje čítať a nastavovať tento skrytý
atribút, ale s možnosťou pridania ďalšej logiky, ako je validácia. Pokus o
vloženie nesprávnej hodnoty do atribútu spôsobí vypísanie chybovej
hlášky a zmena sa nevykoná.
Atribút je stále priamo dostupný aj zvonku, pokiaľ k nemu
pristúpime zápisom clovek._vek = -20
. Pravidlá prístupu k
atribútom zostávajú rovnaké, ako sme si ich ukázali v lekcii Zapúzdrenie
atribútov podrobne.
Kombinácia @property
a @nazev_atributu.setter
je
syntaktická povinnosť, nie iba konvencia. Zatiaľ čo @property
bez setteru použiť je možné, pokus použiť setter bez
@property
spôsobí chybu. Pokiaľ použijeme iba
@property
(hovorí sa mu tiež getter), získame takzvanú
read-only vlastnosť. To znamená, že hodnotu tejto vlastnosti
možno získať (prečítať), ale nemôže byť priamo
nastavená.
Pomocou @property
, teda getteru, dáta čítame a
pomocou @nazev_atributu.setter
, teda setteru, dáta
nastavujeme.
Kedy použiť vlastnosti namiesto metód
Vlastnosti v Pythone nám umožňujú kombinovať výhody metód a prístupu k atribútom. Ponúka sa otázka, kedy je vhodné vlastnosti použiť namiesto klasických metód. Pozrime sa na niekoľko situácií, kedy sú vlastnosti vhodnou voľbou:
- prirodzený prístup k dátam – ak chceme, aby sa prístup k niektorým dátam triedy zdal byť ako k obyčajnému atribútu, ale potrebujeme viac kontroly nad tým, čo sa deje pri čítaní alebo zápise týchto dát,
- zachovanie kompatibility – ak máme existujúcu triedu s verejným atribútom a chceme pridať nejakú logiku pri čítaní alebo zápise tohto atribútu, zmeníme ho na vlastnosť bez nutnosti meniť rozhranie triedy. To znamená, že existujúci kód, ktorý túto triedu využíva, bude fungovať bez zmien,
- validácia dát – vlastnosti sú ideálne na implementáciu validácie dát.
Pozrime sa na doterajší výklad formou komplexnejšieho príkladu.
Predstavme si, že máme triedu Regulator
, ktorá simuluje
teplotný regulátor. Chceme, aby užívateľ mohol nastaviť požadovanú
teplotu, ale zároveň chceme zaistiť, že táto teplota nebude príliš
vysoká ani príliš nízka (napr. medzi 10 °C a 30 °C):
class Regulator:
def __init__(self):
self._teplota = 20 # výchozí teplota
@property
def teplota(self):
return f"Aktuálně je teplota nastavena na {self._teplota}°C."
@teplota.setter
def teplota(self, nova_teplota):
if 10 <= nova_teplota <= 30:
self._teplota = nova_teplota
else:
print(f"Chyba! Teplota {nova_teplota}°C je mimo povolený rozsah (10°C - 30°C).")
def nastav_teplotu_v_kelvinech(self, teplota_k):
teplota_c = teplota_k - 273.15
if 10 <= teplota_c <= 30:
self._teplota = teplota_c
return f"Aktuálně je teplota nastavena na {teplota_k}°K."
else:
return f"Chyba! Teplota {teplota_k}K je mimo povolený rozsah v °C (10°C - 30°C)."
# Použití:
regulator = Regulator()
print(regulator.teplota) # Vypíše: Aktuálně je teplota nastavena na 20°C.
regulator.teplota = 25
print(regulator.teplota)
regulator.teplota = 35 # Vypíše chybu, teplota zůstává 25°C
print(regulator.teplota)
print(regulator.nastav_teplotu_v_kelvinech(298.15))
V tomto rozšírenom príklade používateľ nastavuje teplotu v °C pomocou
vlastnosti @teplota.setter
, ale má tiež možnosť nastaviť
teplotu v kelvinoch pomocou metódy nastav_teplotu_v_kelvinech()
. V
praxi to znamená , že ak existuje nejaká špecifická
funkcia alebo spôsob manipulácie s dátami, ktoré
sa často nepoužívajú alebo sú
zložitejšie, je lepšie použiť tradičnú
metódu.
Pokiaľ však chceme, aby sa niektoré často vykonávané akcie (napr. validácia) vykonávali automaticky pri čítaní alebo nastavovaní hodnoty, sú vlastnosti ideálnym riešením.
Vlastnosť deleter
Vlastností v Pythone je možné nielen čítať a nastavovať, ale aj
mazať. Na tento účel nám Python ponúka možnosť
definovať metódu, ktorá sa vyvolá, keď s našou vlastnosťou použijeme
kľúčové slovo del
. Túto metódu označujeme pomocou
dekorátora @<název_vlastnosti>.deleter
.
Vytvorenie vlastnosti
deleter
Predstavme si nasledujúcu situáciu. Máme triedu, ktorá reprezentuje
dokument. Po zmazaní vlastnosti obsah
chceme vymazať aj hodnotu
atribútu _obsah
, ktorý zastupovala, a nastaviť vlastnosť
smazano
na True
:
class Dokument:
def __init__(self, obsah):
self._obsah = obsah
self.smazano = False
@property
def obsah(self):
if not self.smazano:
return self._obsah
return "Dokument byl smazán."
@obsah.deleter
def obsah(self):
self._obsah = None
self.smazano = True
print("Dokument byl smazán.")
# Použití:
doc = Dokument("Toto je obsah mého dokumentu.")
print(doc.obsah) # Vypíše: Toto je obsah mého dokumentu.
del doc.obsah # Vypíše: Dokument byl smazán.
print(doc.smazano) # Vypíše: True
Zatiaľ čo setter je užitočný pre validáciu hodnôt alebo pre spustenie určitej logiky po nastavení hodnoty, deleter použijeme, ak chceme:
- uvoľniť prostriedky – ak naša trieda spravuje prostriedky, ako sú súbory alebo sieťové spojenia, deleter využijeme na ich uvoľnenie,
- aktualizovať súvisiace atribúty – rovnako ako v našom príklade s dokumentom môže byť potrebné aktualizovať niektoré súvisiace atribúty.
V nasledujúcej lekcii, Vlastnosti v Pythone druhýkrát - Pokročilé vlastnosti a dedenie , sa v práci s vlastnosťami zameriame na dedenie, časté chyby a vytváranie vlastných dekorátorov pre vlastnosti.