IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

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.
Vzhľadom na princípy, na ktorých je Python postavený, vlastnosti neposkytujú z hľadiska zapuzdrenia príliš robustnú ochranu. Stále je našou zodpovednosťou vyvarovať sa priamym zásahom do súkromných alebo privátnych atribútov.

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.
Deleter je v praxi oveľa menej bežný ako gettery a settery. Mnoho programátorov v Pythone ho nikdy nepoužije. Je to preto, že v mnohých prípadoch nie je potrebné explicitne ručiť za mazanie atribútov alebo ich prostriedkov. Ak však máme konkrétny dôvod na jeho použitie, je dobré vedieť, že túto možnosť máme k dispozícii.

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.


 

Predchádzajúci článok
Dekorátory druhýkrát - Parametrické a triedne dekorátory
Všetky články v sekcii
Objektovo orientované programovanie v Pythone
Preskočiť článok
(neodporúčame)
Vlastnosti v Pythone druhýkrát - Pokročilé vlastnosti a dedenie
Článok pre vás napísal gcx11
Avatar
Užívateľské hodnotenie:
1 hlasov
(^_^)
Aktivity