9. diel - Statika v Pythone
V predchádzajúcom cvičení, Riešené úlohy k 5.-8. lekciu OOP v Pythone, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.
V minulej lekcii, Riešené úlohy k 5.-8. lekciu OOP v Pythone , sme si v praxi vyskúšali dedičnosť a polymorfizmus. Dnes sa v Python tutoriálu budeme venovať pojmu statika. Až doteraz sme boli zvyknutí, že dáta (stav) nesie inštancie. Premenné, ktoré sme definovali, teda patrili inštanciu a boli pre každú inštanciu jedinečné. OOP však umožňuje definovať premenné a metódy na samotnej triede. Týmto prvkom hovoríme statické (alebo triedu) a sú nezávislé na inštanciu.
POZOR! Dnešné lekcie vám ukáže statiku, teda postupy,
ktoré v podstate narušujú objektový model. OOP je 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šeobecne by som odporúčal statiku
vôbec nepoužívať, ak si nie ste úplne istí, čo robíte.
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 premenné
Ako triedny môžeme označiť rôzne prvky. Začnime u premenných. Ako som sa už v úvode spomenul, statické prvky patrí triede, nie inštanciu. 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 triedna premenné sú spoločné pre všetky inštancie triedy, ale nie je to presné, pretože s inštanciami naozaj vôbec nesúvisí. Založme si nový súbor (názov napr. Statika) 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_heslo): if self.heslo == zadane_heslo: self.prihlaseny = True return True else: self.prihlaseny = False return False # hesla nesouhlasí
Trieda je pomerne jednoduchá, reprezentuje používateľa nejakého systému. Každá inštancia používateľa má svoje meno, heslo a tiež sa o ňu vie, či je prihlásená alebo nie. Aby sa používateľ prihlásil, zavolá sa na ňom metóda prihlas_se () a v jej parametra 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. V reáli by sa Vaše heslo ešte tzv. Hashovalo, ale to tu vynecháme.
Keď sa používateľ registruje, systém mu napíše, akú minimálnu dĺžku musí jeho heslo mať. Toto číslo by sme mali mať niekde uložené. Vo chvíli, keď používateľa registrujeme, tak ešte nemáme k dispozícii jeho inštanciu. Objekt nie je vytvorený a vytvoria sa až 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í. Údaj uložíme do triednej premennej v triede Uzivatel za pomoci premenné minimalni_delka_hesla:
class Uzivatel: minimalni_delka_hesla = 6 ...
Skúsme si premennú vypísať. K premenné teraz pristúpime priamo cez triedu:
print(Uzivatel.minimalni_delka_hesla)
Vidíme, že premenná naozaj patrí triede. Môžeme sa na ňu pýtať v rôznych miestach programu bez toho, aby sme mali používateľa vytvoreného. Avšak na inštanciu užívateľa túto premennú nájdeme tiež:
u = Uzivatel("Tomáš Marný", "heslojeveslo") print(u.minimalni_delka_hesla)
Pozor! Pri zmene triedne premenné cez inštanciu zmeníme iba hodnotu pre danú inštanciu.
>>> class Trida: promenna = 1 >>> objekt = Trida() >>> objekt.promenna = 2 >>> Trida.promenna 1
Ako ďalšie praktické využitie triednych premenných sa ponúka číslovanie používateľov. Budeme chcieť, aby mal každý užívateľ pridelené unikátne identifikačné číslo. Bez znalosti statiky by sme si museli strážiť zvonku každej vytvorenie užívateľa a počítať je. My si však môžeme vytvoriť priamo na triede Uzivatel privátne statickú premennú dalsi_id, kde bude vždy pripravené číslo pre ďalšieho užívateľa. Prvý užívateľ bude mať id 1, druhý 2 a tak ďalej. Používateľovi teda pribudne nový atribút id, ktorý sa v konstruktoru nastaví podľa hodnoty dalsi_id. Poďme si to vyskúšať:
class Uzivatel: minimalni_delka_hesla = 6 dalsi_id = 1 def __init__(self, jmeno, heslo): self.jmeno = jmeno self.heslo = heslo self.prihlaseny = False self.id = Uzivatel.dalsi_id Uzivatel.dalsi_id += 1 ...
Trieda si sama ukladá, aké bude id ďalší jej inštancie. Toto id priradíme nové inštanciu v konstruktoru a zvýšime ho o 1, aby bolo pripravené pre ďalšiu inštanciu. Statické však nemusí byť len premenné, možnosti sú oveľa väčšie.
Statickej metódy
Statické metódy sa volajú na triede. Ide najmä o pomocné metódy, ktoré potrebujeme často používať a neoplatí sa nám tvoriť inštanciu.
Ukážme si opäť reálny príklad. Pri registrácii používateľa potrebujeme poznať minimálnu dĺžku hesla ešte pred jeho vytvorením. Bolo by tiež dobré, keby sme mohli pred jeho vytvorením aj heslo skontrolovať, či má správnu dĺžku, neobsahuje diakritiku, je v ňom aspoň jedno číslo a podobne. Na tento účel si vytvoríme pomocnú statickú metódu ZvalidujHeslo ():
@staticmethod def zvaliduj_heslo(heslo): if len(heslo) >= Uzivatel.minimalni_delka_hesla: return True else: return False
Opäť si skúsime, že metódu môžeme na triede Uzivatel zavolať:
print(Uzivatel.zvaliduj_heslo("heslojeveslo"));
Pozor! Vďaka tomu, že je metóda zvaliduj_heslo () statická, nemôžeme v nej pristupovať k žiadnym inštančným premenným. Tieto premenné totiž neexistujú v kontexte triedy, ale inštancie. Pýtať sa na meno by v našej metóde nemalo zmysel! Môžete si skúsiť, že to naozaj nejde.
Python obsahuje okrem, statických aj triedne metódy. Táto metódy navyše dostávajú ako prvý parameter triedu. Triedny metódy sa hodí v tom prípade, že budeme triedu dediť a chceme mať v potomkovi inú hodnotu triednej premennej. Inak je lepšie použiť statickú metódu.
@classmethod def zvaliduj_heslo(cls, heslo): if len(heslo) >= cls.__minimalni_delka_hesla: return True else: return False
Prvý parameter obsahujúce odkaz na triedu sa podľa konvencií pomenúva cls. Za pomoci tohto parametra potom voláme triedny premenné, podobne ako sa self.
Rovnaké funkčnosti pri validácii heslá samozrejme môžeme dosiahnuť aj bez znalosti statiky. Vytvorili by sme si nejakú triedu, napr. ValidatorUzivatelu a do nej napísali tieto metódy. Museli by sme potom vytvoriť jej inštanciu, aby sme metódy mohli volať. Bolo by to trochu mätúce, pretože logika užívateľa by bola zbytočne rozdelená do dvoch tried, keď môže byť za pomoci statiky pohromade.
U príklade sa statickou premennou minimalni_delka_hesla sme porušili zapuzdrenie, nemali by sme dovoľovať premennú nekontrolovane meniť. Môžeme ju samozrejme nastaviť ako privátne ak jej čítaní vytvoriť statickú metódu. To ostatne dobre poznáme z minulých dielov. Doplníme takú metódu ik navrátenie id a vyskúšame si ešte nakoniec naše metódy. Koniec programu bude vyzerať takto:
u = Uzivatel("Tomáš Marný", "heslojeveslo") print("ID prvního uživatele je:", u.vrat_id()) v = Uzivatel("Olí Znusinudle", "csfd1fg") print("ID druhého uživatele je:", v.vrat_id()) print("Minimální délka hesla uživatele je:", Uzivatel.vrat_minimalni_delku_hesla()) print('Validnost hesla "heslo" je:', Uzivatel.zvaliduj_heslo("heslo")) input()
A výstup bude:
Od Pythone 3 možno navyše zlučovať "obyčajné funkcie" do tried. Napríklad môžeme použiť:
class Trida: def nejaka_funkce(): print("Tahle funkce je ve třídě.") def jina_funkce(text): print("Tahle funkce je také ve třídě!") print("Text je:", text) Trida.nejaka_funkce() Trida.jina_funkce("parametr")
Vyzerá to podobne, ako by boli funkcie obsiahnuté v nejakom module.
Dodatok k priloženému kódu
Do zdrojového kódu som pre zjednodušenie pridal tieto riadky:
vrat_minimalni_delku_hesla = vrat_minimalni_delku_hesla_s zvaliduj_heslo = zvaliduj_heslo_s
Vytvorí sa nové objekty (funkcie), ktorý sa "zviažu" s pôvodnými funkciami. To nám umožní funkcie použiť bez dodatočného používania písmen na rozlišovanie medzi statickými a triednymi verziami funkcií. Môžete si skúsiť, že obe funkcie odkazujú na rovnaký objekt pomocou operátora is.
Statika sa veľmi často vyskytuje v návrhových vzoroch, o ktorých sme sa tu už bavili. Sú to postupy, ktoré dovádza objektovo orientované programovanie k dokonalosti ao ktorých sa tu určite ešte zmienime. Pre dnešok je toho však už dosť V budúcej lekcii, Riešené úlohy k 9. lekcii OOP v Pythone , sa pozrieme na dátum a čas v Pythone.
V nasledujúcom cvičení, Riešené úlohy k 9. lekcii OOP v Pythone, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.
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é 548x (941 B)
Aplikácia je vrátane zdrojových kódov v jazyku Python