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! 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
:
class Uzivatel:
minimalni_delka_hesla = 6
def __init__(self, jmeno, heslo):
self._jmeno = jmeno
self._heslo = heslo
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ž:
class Uzivatel:
minimalni_delka_hesla = 6
def __init__(self, jmeno, heslo):
self._jmeno = jmeno
self._heslo = heslo
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