9. díl - Statika v Pythonu

Python Objektově orientované programování Statika v Pythonu

V minulém tutoriálu ze seriálu o Pythonu jsme si v praxi vyzkoušeli dědičnost a polymorfismus, dnes se budeme věnovat pojmu statika. Až doposud jsme byli zvyklí, že data (stav) nese instance. Proměnné, které jsme definovali, tedy patřily instanci a byly pro každou instanci jedinečné. OOP však umožňuje definovat proměnné a metody na samotné třídě. Těmto prvkům říkáme statické (nebo třídní) a jsou nezávislé na instanci.

Pozor na statikuPOZOR! Dnešní lekce vám ukáže statiku, tedy postupy, které v podstatě narušují objektový model. OOP je obsahuje jen pro speciální případy a obecně platí, že vše jde napsat bez statiky. Vždy musíme pečlivě zvážit, zda statiku opravdu nutně potřebujeme. Obecně bych doporučoval statiku vůbec nepoužívat, pokud si nejste naprosto jisti, co děláte. Podobně, jako globální proměnné je statika v objektovém programování něco, co umožňuje psát špatný kód a porušovat dobré praktiky. Dnes si ji tedy spíše vysvětlíme. Znalosti použijte s rozvahou, na světe bude potom méně zla.

Třídní proměnné

Jako třídní můžeme označit různé prvky. Začněme u proměnných. Jak jsem se již v úvodu zmínil, statické prvky patří třídě, nikoli instanci. Data v nich uložená tedy můžeme číst bez ohledu na to, zda nějaká instance existuje. V podstatě můžeme říci, že třídní proměnné jsou společné pro všechny instance třídy, ale není to přesné, protože s instancemi doopravdy vůbec nesouvisí. Založme si nový soubor (název např. Statika) a udělejme si jednoduchou třídu 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í

Třída je poměrně jednoduchá, reprezentuje uživatele nějakého systému. Každá instance uživatele má své jméno, heslo a také se o ni ví, zda je přihlášená či nikoli. Aby se uživatel přihlásil, zavolá se na něm metoda prihlas_se() a v jejím parametru heslo, které člověk za klávesnicí zadal. Metoda ověří, zda se jedná opravdu o tohoto uživatele a pokusí se ho přihlásit. Vrátí True/False podle toho, zda přihlášení proběhlo úspěšně. V reálu by se heslo ještě tzv. hashovalo, ale to zde opomineme.

Když se uživatel registruje, systém mu napíše, jakou minimální délku musí jeho heslo mít. Toto číslo bychom měli mít někde uložené. Ve chvíli, kdy uživatele registrujeme, tak ještě nemáme k dispozici jeho instanci. Objekt není vytvořený a vytvoří se až po vyplnění formuláře. Samozřejmě by bylo velmi přínosné, kdybychom měli údaj o minimální délce hesla uložený ve třídě Uzivatel, protože k němu logicky patří. Údaj uložíme do třídní proměnné ve třídě Uzivatel za pomoci proměnné minimalni_del­ka_hesla:

class Uzivatel:

    minimalni_delka_hesla = 6

    ...

Zkusme si proměnnou vypsat. K proměnné nyní přistoupíme přímo přes třídu:

print(Uzivatel.minimalni_delka_hesla)

Vidíme, že proměnná opravdu náleží třídě. Můžeme se na ni ptát v různých místech programu bez toho, aniž bychom měli uživatele vytvořeného. Avšak na instanci uživatele tuto proměnnou nalezneme také:

u = Uzivatel("Tomáš Marný", "heslojeveslo")
print(u.minimalni_delka_hesla)

Pozor! Při změně třídní proměnné přes instanci změníme pouze hodnotu pro danou instanci.

>>> class Trida:
        promenna = 1
>>> objekt = Trida()
>>> objekt.promenna = 2
>>> Trida.promenna
1

Jako další praktické využití třídních proměnných se nabízí číslování uživatelů. Budeme chtít, aby měl každý uživatel přidělené unikátní identifikační číslo. Bez znalosti statiky bychom si museli hlídat zvenčí každé vytvoření uživatele a počítat je. My si však můžeme vytvořit přímo na třídě Uzivatel privátní statickou proměnnou dalsi_id, kde bude vždy připraveno číslo pro dalšího uživatele. První uživatel bude mít id 1, druhý 2 a tak dále. Uživateli tedy přibude nový atribut id, který se v konstruktoru nastaví podle hodnoty dalsi_id. Pojďme si to vyzkoušet:

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

    ...

Třída si sama ukládá, jaké bude id další její instance. Toto id přiřadíme nové instanci v konstruktoru a zvýšíme ho o 1, aby bylo připraveno pro další instanci. Statické však nemusí být jen proměnné, možnosti jsou mnohem větší.

Statické metody

Statické metody se volají na třídě. Jedná se zejména o pomocné metody, které potřebujeme často používat a nevyplatí se nám tvořit instanci.

Ukažme si opět reálný příklad. Při registraci uživatele potřebujeme znát minimální délku hesla ještě před jeho vytvořením. Bylo by také dobré, kdybychom mohli před jeho vytvořením i heslo zkontrolovat, zda má správnou délku, neobsahuje diakritiku, je v něm alespoň jedno číslo a podobně. Za tímto účelem si vytvoříme pomocnou statickou metodu ZvalidujHeslo():

@staticmethod
def zvaliduj_heslo(heslo):
    if len(heslo) >= Uzivatel.minimalni_delka_hesla:
        return True
    else:
        return False

Opět si zkusíme, že metodu můžeme na třídě Uzivatel zavolat:

print(Uzivatel.zvaliduj_heslo("heslojeveslo"));

Pozor! Díky tomu, že je metoda zvaliduj_heslo() statická, nemůžeme v ní přistupovat k žádným instančním proměnným. Tyto proměnné totiž neexistují v kontextu třídy, ale instance. Ptát se na jmeno by v naší metodě nemělo smysl! Můžete si zkusit, že to opravdu nejde.

Python obsahuje kromě, statických i třídní metody. Tato metody navíc dostávají jako první parametr třídu. Třídní metody se hodí v tom případě, že budeme třídu dědit a chceme mít v potomkovi jinou hodnotu třídní proměnné. Jinak je lepší použít statickou metodu.

@classmethod
def zvaliduj_heslo(cls, heslo):
    if len(heslo) >= cls.__minimalni_delka_hesla:
        return True
    else:
        return False

První parametr obsahující odkaz na třídu se podle konvencí pojmenovává cls. Za pomoci tohoto parametru potom voláme třídní proměnné, podobně jako se self.

Stejné funkčnosti při validaci hesla samozřejmě můžeme dosáhnout i bez znalosti statiky. Vytvořili bychom si nějakou třídu, např. ValidatorUzivatelu a do ní napsali tyto metody. Museli bychom poté vytvořit její instanci, abychom metody mohli volat. Bylo by to trochu matoucí, protože logika uživatele by byla zbytečně rozdělena do dvou tříd, když může být za pomoci statiky pohromadě.

U příkladu se statickou proměnnou minimalni_del­ka_hesla jsme porušili zapouzdření, neměli bychom dovolovat proměnnou nekontrolovaně měnit. Můžeme ji samozřejmě nastavit jako privátní a k jejímu čtení vytvořit statickou metodu. To ostatně dobře známe z minulých dílů. Doplníme takovou metodu i k navrácení id a vyzkoušíme si ještě nakonec naše metody. Konec programu bude vypadat 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:

Statické, třídní metody a atributy v Pythonu

Od Pythonu 3 lze navíc slučovat "obyčejné funkce" do tříd. Například můžeme použít:

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")

Vypadá to podobně, jako by byly funkce obsažené v nějakém modulu.

Dodatek k přiloženému kódu

Do zdrojového kódu jsem pro zjednodušení přidal tyto řádky:

vrat_minimalni_delku_hesla = vrat_minimalni_delku_hesla_s
zvaliduj_heslo = zvaliduj_heslo_s

Vytvoří se nové objekty (funkce), jenž se "svážou" s původními funkcemi. To nám umožní funkce použít bez dodatečného používání písmen na rozlišování mezi statickými a třídními verzemi funkcí. Můžete si zkusit, že obě funkce odkazují na stejný objekt pomocí operátoru is.

Statika se velmi často vyskytuje v návrhových vzorech, o kterých jsme se zde již bavili. Jsou to postupy, které dovádí objektově orientované programování k dokonalosti a o kterých se tu jistě ještě zmíníme. Pro dnešek je toho však již dost :) Příště se podíváme na vlastnosti v Pythonu.


 

Stáhnout

Staženo 63x (1.91 kB)
Aplikace je včetně zdrojových kódů v jazyce python

 

  Aktivity (1)

Článek pro vás napsal gcx11
Avatar
(^_^)

Jak se ti líbí článek?
Celkem (2 hlasů) :
4.54.54.54.54.5


 



 

 

Komentáře

Avatar
Člen
Člen
Avatar
Člen:

Zase vydarený článok zo série Python ;)

Odpovědět  +1 18.9.2014 18:45
...
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 1 zpráv z 1.