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

13. diel - Statika v Pythone druhýkrát - Statické a triedne metódy

V minulej lekcii, Statika v Pythone - Triedne atribúty , sme si vysvetlili statiku v Pythone.

V tomto tutoriále objektovo orientovaného programovania v Pythone budeme pokračovať v práci so statikou. Vysvetlíme si statické a triedne metódy.

Statické metódy

Statické metódy sa podobne ako triedne atribúty volajú na triede. Sú to metódy, ktoré síce patria triede, ale nie sú zviazané s konkrétnou inštanciou. Táto vlastnosť z nich robí ideálnych kandidátov pre pomocné metódy, ktoré sú často potrebné, ale nevyžadujú kontext jednotlivých inštancií.

Pozrime sa opäť na reálny príklad. Pri registrácii nového 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. Za týmto účelom si vytvoríme pomocnú statickú metódu zvaliduj_heslo(), ktorú si zavoláme na triede Uzivatel. Statická metóda zvaliduj_heslo() v triede Uzivatel môže pristupovať k triednemu atribútu minimalni_delka_hesla pomocou názvu triedy, čo demonštruje, ako statická metóda pracuje s triednymi dátami bez nutnosti vytvárať inštanciu triedy:

class Uzivatel:

    minimalni_delka_hesla = 6

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

    @staticmethod
    def zvaliduj_heslo(heslo):
        if len(heslo) >= Uzivatel.minimalni_delka_hesla:  #  metoda přistupuje k třídnímu atributu přes název třídy, ne instance.
            return True
        else:
            return False

print(Uzivatel.zvaliduj_heslo("heslojeveslo"))

Pozor! Vzhľadom k tomu, že zvaliduj_heslo() je statická metóda, nemožno v nej pristupovať k inštančným atribútom triedy Uzivatel. Inštančné atribúty, ako je _jmeno, sú spojené s konkrétnou inštanciou a statická metóda operuje nezávisle od akejkoľvek inštancie. Statické metódy môžu pristupovať iba k triednym atribútom, ktoré sú definované na úrovni triedy a sú rovnaké pre všetky inštancie. To znamená, že v rámci zvaliduj_heslo() môžeme pracovať s triednym atribútom minimalni_delka_hesla, ale nie je možné tu pristupovať k inštančným atribútom ako je _jmeno.

Dekorátor @staticmethod

Zhrňme si znalosti, ktoré sme zatiaľ získali a povedzme si niekoľko detailov k použitému dekorátoru. Dekorátor @staticmethod sa v Pythone používa na označenie metódy triedy, ktorá nevyžaduje referenciu na konkrétnu inštanciu triedy (self) ani na samotnú triedu (cls - viac si povieme za chvíľu). Inými slovami, statická metóda pracuje nezávisle od inštancií triedy a jej správanie sa nemení na základe stavu inštancie alebo triedy.

Dekorátory v Pythone predstavujú spôsob, ako modifikovať alebo rozširovať funkčnosť funkcií alebo tried bez nutnosti meniť ich pôvodný kód. Okrem užívateľsky definovaných dekorátorov Python ponúka aj rad vstavaných dekorátorov, ktoré uľahčujú bežné úlohy v objektovo orientovanom programovaní, napríklad prácu s triednymi alebo statickými metódami. O dekorátoroch si podrobne povieme neskôr v kurze.

Hlavné výhody použitia @staticmethod:

  • zrejmosť - keď vidíme v kóde dekorátor @staticmethod, je jasné, že táto metóda nezávisí od stavu inštancie ani triedy,
  • výkon - pretože @staticmethod nevyžaduje odovzdanie self alebo cls, býva volanie tejto metódy o niečo rýchlejšie.
Kedy @staticmethod používame

Statickú metódu teda použijeme, keď:

  • metóda nevyžaduje prístup k žiadnym atribútom alebo metódam triedy,
  • metóda nevyžaduje zmenu v triede alebo jej inštanciách,
  • máme logickú funkciu, ktorá by sa vhodne hodila do triedy (napr. kvôli organizácii kódu), ale nevyužíva konkrétne vlastnosti triedy.
Ukážme si príklad. Majme triedu Geometrie, ktorá bude obsahovať niekoľko statických metód spojených s geometrickými výpočtami:
import math

class Geometrie:

    @staticmethod
    def obvod_kruhu(polomer):
        return round((2 * math.pi * polomer), 2)

    @staticmethod
    def obvod_obdelnika(delka, sirka):
        return 2 * (delka + sirka)

    @staticmethod
    def je_ctverec(delka, sirka):
        return delka == sirka

# Použití statických metod
print(Geometrie.obvod_kruhu(5))
print(Geometrie.obvod_obdelnika(5, 3))
print(Geometrie.je_ctverec(5, 5))
print(Geometrie.je_ctverec(5, 3))

Vo výstupe konzoly uvidíme:

Použití statických metod:
31.42
16
True
False

Prečo vôbec @staticmethod používame

V Pythone je síce technicky možné definovať metódy vo vnútri triedy bez použitia @staticmethod dekorátora, ale na jeho použitie máme niekoľko pádnych argumentov:

  • zrejmosť - Už sme si povedali, že dekorátor @staticmethod jasne hovorí, že metóda nemá prístup k inštančným atribútom/metódam. Keď tento dekorátor iní vývojári vidia, okamžite vedia, čo od tej metódy môžu očakávať.
  • organizácie a design - Použitie @staticmethod nám pomáha pri organizácii kódu.
  • rozšíriteľnosť - Ak sa neskôr rozhodneme, že chceme pridať nejaké triedne alebo inštančné atribúty/metódy, ktoré by mohli interagovať s našimi statickými metódami, je oveľa jednoduchšie a čistejšie mať už štruktúru, ktorá rozlišuje medzi statickými a ne-statickými metódami.
  • odovzdávanie self a cls - Keď definujeme metódu v triede bez @staticmethod, prvý argument je automaticky považovaný za referenciu na inštanciu (self) alebo na triedu (cls), ak je to triedna metóda (o tých si povieme za chvíľu). Ľahko tak narazíme na problémy s nesprávnym počtom argumentov pri volaní funkcie:
class Geometrie:
    def obvod_kruhu(polomer):  # metoda BEZ dekorátoru
        return 2 * math.pi * polomer

print(Geometrie.obvod_kruhu(5)) # Funguje správně - i když metoda nemá dekorátor, volání přímo na třídě je možné
g = Geometrie()
print(g.obvod_kruhu(5))         # Způsobí chybu

Vo výstupe konzoly uvidíme:

Použití statických metod bez dekorátoru:
31.41592653589793
...
TypeError: Geometrie.obvod_kruhu() takes 1 positional argument but 2 were given

Dekorátor @staticmethod teda umožňuje metódu volať ako cez triedu, tak cez inštanciu bez toho, aby bolo nutné dodávať self:

class Geometrie:
    @staticmethod
    def obvod_kruhu(polomer):
        return 2 * math.pi * polomer

print(Geometrie.obvod_kruhu(5))  # Funguje správně
g = Geometrie()
print(g.obvod_kruhu(5))         # Také funguje správně

Skrátka, aj keď je možné vytvárať "statické" metódy v Pythone bez použitia @staticmethod, dekorátor nám poskytuje oveľa väčšiu flexibilitu, čitateľnosť a robustnosť pri práci s triedami.

Utility (helper) triedy

Keď sa zamyslíme nad tým, čo sme si doteraz povedali, vyplýva nám jedno zaujímavé zistenie. Python nám umožňuje vytvárať akési kontajnery (obvykle súvisiacich) metód, združených v jednej triede. Takéto triede sa hovorí utility trieda (alebo tiež helper trieda). Ich hlavnou funkciou je usporiadať súbor súvisiacich funkcií do jednej logickej jednotky, čo nám pomáha k lepšej organizácii kódu a lepšej čitateľnosti. Za príklad nám poslúži naša trieda Geometrie, avšak bez dekorátora @staticmethod as upraveným názvom GeometrieUtilities popisujúcim jej účel:

class GeometrieUtilities:

    def obvod_kruhu(polomer):
        return round((2 * math.pi * polomer), 2)

    def obvod_obdelnika(delka, sirka):
        return 2 * (delka + sirka)

    def je_ctverec(delka, sirka):
        return delka == sirka

print(GeometrieUtilities.obvod_kruhu(5))

Týmto spôsobom si teda vieme vytvoriť kontajner metód, ktoré sú na prvý pohľad statické a treba len pamätať na to, že taká utility trieda neslúži na vytváranie inštancií, ale skôr ako sada nástrojov, alebo priamo knižnica. Hoci neexistuje pevná konvencia pre pomenovanie tohto typu tried, odporúčame v názve zohľadniť ich podstatu. Tak, ako sme urobili v našom príklade s triedou GeometrieUtilities. Mnoho štandardných knižníc v Pythone obsahuje moduly, ktoré v podstate fungujú ako utility triedy, pretože poskytujú sady funkcií či metód bez potreby vytvárať inštancie.

Pozor! Utility triedy neinštancujeme. Volanie metódy utility triedy z jej inštancie spôsobí chybu.

Triedne metódy

Python obsahuje okrem statických aj triedne metódy. A tu sa konečne dostávame k onomu tajomnému cls:-) Prvý parameter triednej metódy vždy obsahuje odkaz na triedu a podľa konvencií sa pomenováva cls. Za pomoci tohto parametra potom voláme triedne atribúty, podobne ako so self. Triedne metódy pracujú s triednymi atribútmi a nie s inštančnými atribútmi. Označujeme ich pomocou dekorátora @classmethod. Triedne metódy sa hodia v tom prípade, že budeme triedu dediť a chceme mať v potomkovi inú hodnotu triedneho atribútu. Inak je lepšie použiť statickú metódu. Najlepšie bude, keď si ukážeme príklad:

class Rodic:
    hodnota = "rodič"

    @classmethod
    def vrat_hodnotu(cls):  # cls odkazuje na aktuální třídu (buď Rodic nebo Potomek, pokud je tato metoda volána z potomka)
        return cls.hodnota

    @staticmethod
    def staticka_metoda():
        return "Jsem statická metoda, zůstanu stejná i pokud mě někdo zdědí."

class Potomek(Rodic):
    hodnota = "Jsem potomek, ne rodič."

print(Potomek.vrat_hodnotu())
print(Potomek.staticka_metoda())

V konzole uvidíme:

Třídní metody:
Jsem potomek, ne rodič.
Jsem statická metoda, zůstanu stejná i pokud mě někdo zdědí.

V našom príklade voláme triednu metódu vrat_hodnotu() na potomkovi. Metóda nám vráti hodnotu atribútu hodnota z triedy Potomek, nie z triedy Rodic. Ale pozor, staticka_metoda() nemá žiadny prístup k triednym atribútom, takže nezáleží na tom, či je volaná z rodiča či potomka – vždy bude vracať rovnaký výstup.

Rozdiely medzi triednymi a statickými metódami

Pozrime sa stručný prehľad rozdielov medzi triednymi a statickými metódami:

1. Dekorátor:

  • Triedna metóda používa dekorátor @classmethod.
  • Statická metóda používa dekorátor @staticmethod.
2. Prvý parameter:
  • Triedna metóda - prvým parametrom je vždy odkaz na triedu, obvykle pomenovaný cls.
  • Statická metóda - nemá žiadny špeciálny prvý parameter. Chová sa ako bežná funkcia, ktorá je iba definovaná vo vnútri triedy.
3. Dedičnosť:
  • Triedna metóda - pokiaľ je trieda dedená, triedna metóda v potomkoch bude pracovať s triednymi atribútmi daného potomka,
  • Statická metóda - je nezávislá od dedenia. Či je volaná z rodičovskej triedy alebo z potomka, chová sa vždy rovnako.
4. Použitie:
  • Triedna metóda - je užitočná, keď potrebujeme pracovať s triednymi atribútmi alebo keď chceme, aby metóda bola upraviteľná v dedených triedach.
  • Statická metóda - je vhodná, keď potrebujeme vykonať nejakú operáciu, ktorá súvisí s triedou, ale nevyžaduje si prístup k jej atribútom. Je to v podstate funkcia, ktorá nesúvisí s konkrétnou inštanciou triedy.
Upravme si na záver triedu Uzivatel, ktorá nás sprevádza od začiatku témy statiky:
class Uzivatel:
    minimalni_delka_hesla = 6  # třídní atribut

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

    @staticmethod
    def zvaliduj_heslo(heslo):
        return len(heslo) >= Uzivatel.minimalni_delka_hesla

    @classmethod
    def nastav_minimalni_delka_hesla(cls, nova_delka):
        cls.minimalni_delka_hesla = nova_delka

    def je_heslo_validni(self):
        return self.zvaliduj_heslo(self._heslo)

class VIPUzivatel(Uzivatel):
    minimalni_delka_hesla = 10  # VIP uživatelé mají delší minimální délku hesla

    @staticmethod
    def zvaliduj_heslo(heslo):
        return len(heslo) >= VIPUzivatel.minimalni_delka_hesla
    @classmethod
    def informace_o_hesle(cls):
        return f"Minimální délka hesla pro {cls.__name__} je {cls.minimalni_delka_hesla} znaků."

tomas = Uzivatel('Tomáš', 'motyka')
petr = VIPUzivatel('Petr', 'veslo12')

print(tomas.zvaliduj_heslo('test'))  # Výstup: False - heslo je krátké

print(petr.informace_o_hesle())  # Výstup: Minimální délka hesla pro VIPUzivatel je 10 znaků.

print(tomas.je_heslo_validni())  # Výstup: True
print(petr.je_heslo_validni())   # Výstup: False

V príklade máme metódy:

  • zvaliduj_heslo() - statická metóda a je možné ju volať aj cez inštanciu, ale jej logika závisí iba na konkrétnej triede Uzivatel, nie na tom, aká inštancia (bežný užívateľ alebo VIP) ju volá. Metóda je pre VIP používateľa preťažená kvôli dĺžke hesla.
  • informace_o_hesle() - triedna metóda v triede VIPUzivatel a keď ju voláme cez inštanciu, vracia informácie špecifické pre triedu tej inštancie (VIPUzivatel v našom prípade).
  • je_heslo_validni() - metóda inštancie, ktorá využíva statickú metódu zvaliduj_heslo(). Demonštruje, ako metóda inštancie dokáže využívať statické aj triedne metódy.
To je pre túto lekciu všetko.

V nasledujúcom kvíze, Kvíz - Statika v Pythone, si vyskúšame 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é 37x (1.08 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python

 

Predchádzajúci článok
Statika v Pythone - Triedne atribúty
Všetky články v sekcii
Objektovo orientované programovanie v Pythone
Preskočiť článok
(neodporúčame)
Kvíz - Statika v Pythone
Článok pre vás napísal Karel Zaoral
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Karel Zaoral
Aktivity