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

12. diel - Statika v Pythone - Triedne atribúty

V predchádzajúcom cvičení, Riešené úlohy k 8.-11. lekciu OOP v Pythone, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V nasledujúcom tutoriále objektovo orientovaného programovania v Pythone sa budeme venovať pojmu statika. Až doteraz sme boli zvyknutí, že dáta (stav) nesie inštancie. Premenné (atribúty), ktoré sme definovali, teda patrili inštancii a boli pre každú inštanciu jedinečné. OOP však umožňuje definovať atribúty a metódy na samotnej triede. Týmto prvkom hovoríme statické (alebo tiež triedne) a sú nezávislé na inštancii. Najprv sa zameriame na triedne atribúty.

Pozor na statiku - Objektovo orientované programovanie v Pythone - Objektovo orientované programovanie v Pythone
POZOR! Dnešná lekcia vám ukáže statiku, teda postupy, ktoré v podstate narúšajú objektový model. OOP ich obsahuje len pre špeciálne prípady a všeobecne platí, že všetko ide napísať bez statiky. Vždy musíme starostlivo zvážiť, či statiku naozaj nutne potrebujeme. Všeobecné odporúčanie je statiku vôbec nepoužívať, pokiaľ si nie sme úplne istí, čo robíme. Podobne ako globálne premenné je statika v objektovom programovaní niečo, čo umožňuje písať zlý kód a porušovať dobré praktiky. Dnes si ju teda skôr vysvetlíme. Znalosti použite s rozvahou, na svete bude potom menej zla.

Triedne atribúty

Ako triedne môžeme označiť rôzne prvky. Začnime pri atribútoch. Ako sme už v úvode spomenuli, statické prvky patria triede, nie inštancii. Dáta v nich uložené teda môžeme čítať bez ohľadu na to, či nejaká inštancia existuje. V podstate môžeme povedať, že triedne atribúty sú zdieľané medzi všetkými inštanciami triedy. Sú definované vo vnútri triedy, ale mimo akúkoľvek metódu a existujú aj pred vytvorením akejkoľvek inštancie triedy. Vysvetlíme si to na príklade. Predstavme si, že potrebujeme zdieľať nejaký údaj medzi všetkými inštanciami triedy alebo chceme, aby bol tento údaj dostupný, aj keď ešte neexistuje žiadna inštancia triedy. Založme si nový súbor (názov napr. statika.py) a urobme si jednoduchú triedu Uzivatel:

class Uzivatel:

    def __init__(self, jmeno, heslo):
        self._jmeno = jmeno
        self._heslo = heslo
        self._prihlaseny = False

    def prihlas_se(self, zadane_jmeno, zadane_heslo):
        if self._jmeno == zadane_jmeno and self._heslo == zadane_heslo:
            self._prihlaseny = True
            return True
        else:
            self._prihlaseny = False
            return False  # jméno nebo heslo nesouhlasí

Trieda je pomerne jednoduchá. Reprezentuje používateľov nejakého systému. Každá inštancia používateľa má svoje meno, heslo a tiež sa o nej vie, či je prihlásená alebo nie. Aby sa užívateľ prihlásil, zavolá sa na ňom metóda prihlas_se(). Tá nesie v parametri meno a heslo, ktoré človek za klávesnicou zadal. Metóda overí, či ide naozaj o tohto používateľa a pokúsi sa ho prihlásiť. Vráti True/False podľa toho, či prihlásenie prebehlo úspešne.

Ako do toho zapojíme triedny atribút? Keď sa nový užívateľ registruje, systém mu napíše, akú minimálnu dĺžku musí jeho heslo mať. Toto číslo ale musíme mať niekde uložené. Lenže vo chvíli, keď užívateľa registrujeme, ešte nemáme jeho inštanciu k dispozícii. Objekt skrátka nie je vytvorený a vytvorí sa až na základe dát získaných po vyplnení formulára. Samozrejme by bolo veľmi prínosné, keby sme mali údaj o minimálnej dĺžke hesla uložený v triede Uzivatel, pretože k nemu logicky patrí. Ako to teda vyriešime? Údaj uložíme priamo v triede Uzivatel do triedneho atribútu. Vytvoríme si k tomu atribút minimalni_delka_hesla:

class Uzivatel:

    minimalni_delka_hesla = 6

    def __init__(self, jmeno, heslo):
    ...
    def prihlas_se(self, zadane_jmeno, zadane_heslo):
    ...

Až doteraz sme všetky dáta objektu pridávali až pri vzniku jeho inštancie pomocou konštruktora. Statika nám poskytuje riešenie, ako objekt vybaviť dátami ešte predtým, než vôbec vznikne akákoľvek jeho inštancia.

Poďme si atribút vypísať. K triednemu atribútu pristúpime priamo cez triedu, syntax je NazevTridy.nazev_atributu:

print(Uzivatel.minimalni_delka_hesla)  # všimneme si velkého písmena v názvu třídy - skutečně tedy nejde o instanci

Vidíme, že atribút skutočne patrí triede. Môžeme sa na neho pýtať v rôznych miestach programu bez toho, aby sme mali užívateľa vytvoreného. Ale pozor, na inštancii používateľa tento atribút nájdeme tiež:

novy_uzivatel = Uzivatel("Tomáš Marný", "heslojeveslo")
print(novy_uzivatel.minimalni_delka_hesla)            # malé "n" v novy_uzivatel značí, že skutečně pracujeme s instancí

Vidíme teda, že triedne atribúty zdieľajú svoje hodnoty naprieč všetkými inštanciami danej triedy. Ale pozor! Pri zmene triedneho atribútu v inštancii zmeníme iba hodnotu pre danú inštanciu.

Pozrime sa na príklad:

class Trida:
    tridni_atribut = 'Zde jsou data třídního atributu, která jsou dostupná kdykoliv bez vytvoření instance.'

instance = Trida()

print(instance.tridni_atribut)
instance.tridni_atribut = 'Zde měníme hodnotu třídního atributu v instanci.'
print(instance.tridni_atribut)
print(Trida.tridni_atribut)

Vo výstupe konzoly uvidíme:

Třídní atribut:
Zde jsou data třídního atributu, která jsou stále dostupná kdykoliv bez vytvoření instance.
Zde měníme hodnotu třídního atributu v instanci.
Zde jsou data třídního atributu, která jsou stále dostupná kdykoliv bez vytvoření instance.

Ako ďalšie praktické využitie triednych atribútov sa ponúka číslovanie užívateľov. Budeme chcieť, aby mal každý používateľ pridelené unikátne identifikačné číslo. Bez znalosti statiky by sme si museli strážiť zvonku každé vytvorenie nového užívateľa a počítať ich. My si však vytvoríme priamo na triede Uzivatel statický (= triedny) atribút dalsi_id, kde bude vždy pripravené číslo pre ďalšieho používateľa. Prvý používateľ bude mať id = 1, druhý 2 a tak ďalej. Užívateľovi teda pribudne nový atribút id, ktorý sa v konštruktore nastaví podľa hodnoty dalsi_id. Poďme si to vyskúšať:

class Uzivatel:
    minimalni_delka_hesla = 6   # třídní atribut
    dalsi_id = 1                # třídní atribut

    def __init__(self, jmeno, heslo):
        self._jmeno = jmeno
        self._heslo = heslo
        self._prihlaseny = False
        self._id = Uzivatel.dalsi_id   # přidělíme aktuální id
        Uzivatel.dalsi_id += 1        # připravíme id pro další instanci

uzivatel_admin = Uzivatel("Tomáš Správce", "adminheslo")
uzivatel_obycejny = Uzivatel("Tomáš Uživatel", "heslouzivatele")

print(f"ID uživatele {uzivatel_admin._jmeno} je {uzivatel_admin._id}")
print(f"ID uživatele {uzivatel_obycejny._jmeno} je {uzivatel_obycejny._id}")

Trieda si sama ukladá, aké bude id jej inštancia. Toto id priradíme novej inštancii v konštruktore a zvýšime ho o 1, aby bolo pripravené pre ďalšiu inštanciu.

Špecifiká dynamicky typovaného jazyka

Python je dynamicky typovaný jazyk. To znamená, že jeho možnosti sú oproti statickým jazykom (typicky C#) trochu širšie. Pozrime sa na konkrétne príklady.

Dynamické priradenie v Pythone

V Pythone vieme vytvoriť triedne atribúty za behu. Ide o rovnaký mechanizmus, aký sme si popísali v lekcii Zapúzdrenie atribútov podrobne v Pythone. Teda aj keď neexistovali pri definícii triedy. To, ako už vieme, je niečo, čo v jazykoch s pevnými definíciami tried, ako je C#, nie je možné. Pozrime sa na príklad:

class Trida:
    pass

# nějaký kód programu
# zjistili jsme, že by se nám hodil třídní atribut. Tak si jej vytvoříme:
Trida.novy_tridni_atribut = "Toto je nový třídní atribut!"

Pretypovanie

Toto je síce zrejmé z povahy Pythona, ale rovnako sa o typovaní zmienime. Typ triedneho atribútu je možné ľahko zmeniť za behu:

class Clovek:
    vek = 30

print(f"Původní věk: {Clovek.vek} (typ {type(Clovek.vek).__name__})")

# Nyní změníme typ třídního atributu 'vek' z čísla na řetězec
Clovek.vek = "Třicet let"

print(f"Po přetypování: {Clovek.vek} (typ {type(Clovek.vek).__name__})")

Funkcia type() vracia triedu (alebo typ) objektu, to už poznáme. Keď ale chceme získať iba meno triedy ako reťazec (v našom príklade int), použijeme syntax s magickým atribútom .__name__. O "mágii":-) v Pythone si povieme neskôr v kurze.

Pretypovanie je v niektorých prípadoch užitočné (napríklad keď potrebujeme upraviť správanie objektu počas behu), ale môže tiež viesť k chybám. Typicky pokiaľ nečakáme (zabudneme), že sa typ atribútu zmenil v priebehu životného cyklu programu a pokúsime sa s ním pracovať ako s pôvodným typom int.

To je pre túto lekciu všetko.

V budúcej lekcii, Statika v Pythone druhýkrát - Statické a triedne metódy , dokončíme tému statiky. Preberieme statické a triedne metódy.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 569x (941 B)
Aplikácia je vrátane zdrojových kódov v jazyku Python

 

Predchádzajúci článok
Riešené úlohy k 8.-11. lekciu OOP v Pythone
Všetky články v sekcii
Objektovo orientované programovanie v Pythone
Preskočiť článok
(neodporúčame)
Statika v Pythone druhýkrát - Statické a triedne metódy
Článok pre vás napísal gcx11
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
(^_^)
Aktivity