14. diel - Statika v Pythone druhýkrát - Statické a triedne metódy
V predchádzajúcej lekcii, Statika v Pythone - Triedne atribúty, sme si vysvetlili statiku v Pythone.
V tomto tutoriáli 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
skontrolovať, či má heslo 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 validate_password(),
ktorú si zavoláme na triede User. Statická metóda
validate_password() v triede User môže pristupovať
k triednemu atribútu minimal_password_length pomocou
názvu triedy, čo demonštruje, ako statická metóda pracuje s
triednymi dátami bez nutnosti vytvárať inštanciu triedy:
class User:
minimal_password_length = 6
def __init__(self, name, password):
self._name = name
self._password = password
self._logged_in = False
@staticmethod
def validate_password(password):
return len(password) >= User.minimal_password_length # the method accesses the class attribute via the class name, not the instance.
print(User.validate_password("passwordissword"))
Pozor! Vzhľadom na to, že validate_password()
je statická metóda, nemožno v nej pristupovať k inštančným atribútom
triedy User. Inštančné atribúty, ako je _name, 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 validate_password() môžeme
pracovať s triednym atribútom minimal_password_length, ale nie je
tu možné pristupovať k inštančným atribútom ako je
_name.
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 niekoľko 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
@staticmethodnevyžaduje odovzdanieselfalebocls, volanie tejto metódy býva 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 hodila do triedy (napr. kvôli organizácii kódu), ale nevyužíva konkrétne vlastnosti triedy.
Ukážme si príklad. Majme triedu Geometry, ktorá bude
obsahovať niekoľko statických metód spojených s geometrickými
výpočtami:
import math class Geometry: @staticmethod def circle_circumference(radius): return round((2 * math.pi * radius), 2) @staticmethod def rectangle_perimeter(length, width): return 2 * (length + width) @staticmethod def is_square(length, width): return length == width # Using static methods print(Geometry.circle_circumference(5)) print(Geometry.rectangle_perimeter(5, 3)) print(Geometry.is_square(5, 5)) print(Geometry.is_square(5, 3))
Vo výstupe konzoly uvidíme:
Using static methods:
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
@staticmethodjasne 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
@staticmethodná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
selfacls- 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 Geometry: def circle_circumference(radius): # method WITHOUT decorator return 2 * math.pi * radius print(Geometry.circle_circumference(5)) # Works correctly - even though the method has no decorator, calling it directly on the class is possible g = Geometry() print(g.circle_circumference(5)) # Causes an error
Vo výstupe konzoly uvidíme:
Using static methods without a decorator:
31.41592653589793
...
TypeError: Geometry.circle_circumference() 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é pridávať
self:
class Geometry: @staticmethod def circle_circumference(radius): return 2 * math.pi * radius print(Geometry.circle_circumference(5)) # Works correctly g = Geometry() print(g.circle_circumference(5)) # Also works correctly
Jednoducho, 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, príde nám na um
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. Takejto 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. Ako príklad nám poslúži naša trieda
Geometry, avšak bez dekorátora @staticmethod a s
upraveným názvom GeometryUtilities popisujúcim jej účel:
class GeometryUtilities: def circle_circumference(radius): return round((2 * math.pi * radius), 2) def rectangle_perimeter(length, width): return 2 * (length + width) def is_square(length, width): return length == width print(GeometryUtilities.circle_circumference(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 súbor 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 to urobili v
našom príklade s triedou GeometryUtilities. Mnoho štandardných
knižníc v Pythone obsahuje moduly, ktoré v podstate fungujú ako utility
triedy, pretože poskytujú súbory 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 nášmu tajomnému cls
Prvý parameter triednej metódy
vždy obsahuje odkaz na triedu a podľa konvencií sa pomenováva
cls. S pomocou 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 Parent: value = "parent" @classmethod def get_value(cls): # cls refers to the current class (either Parent or Child, if this method is called from Child) return cls.value @staticmethod def static_method(): return "I am a static method. I remain the same even if inherited." class Child(Parent): value = "I am the child, not the parent." print(Child.get_value()) print(Child.static_method())
V konzole uvidíme:
Class methods:
I am the child, not the parent.
I am a static method. I remain the same even if inherited.
V našom príklade voláme triednu metódu get_value() na
potomkovi. Metóda nám vráti hodnotu atribútu value z triedy
Child, nie z triedy Parent. Ale
pozor, static_method() 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, zvyčajne
pomenovaný
cls. - Statická metóda - nemá žiadny špeciálny prvý parameter. Správa sa ako bežná funkcia, ktorá je iba definovaná vo vnútri triedy.
3. Dedičnosť:
- Triedna metóda - ak 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, správa 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 sa metóda dala upraviť 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 User, ktorá nás sprevádza od
začiatku témy statiky:
class User:
minimal_password_length = 6 # class attribute
def __init__(self, name, password):
self._name = name
self._password = password
@staticmethod
def validate_password(password):
return len(password) >= User.minimal_password_length
@classmethod
def set_minimal_password_length(cls, new_length):
cls.minimal_password_length = new_length
def is_password_valid(self):
return self.validate_password(self._password)
class VIPUser(User):
minimal_password_length = 10 # VIP users have a longer minimum password length
@staticmethod
def validate_password(password):
return len(password) >= VIPUser.minimal_password_length
@classmethod
def password_info(cls):
return f"The minimum password length for {cls.__name__} is {cls.minimal_password_length} characters."
thomas = User('Thomas', 'shovel')
peter = VIPUser('Peter', 'rowboat12')
print(thomas.validate_password('test')) # Output: False - the password is too short
print(peter.password_info()) # Output: The minimum password length for VIPUser is 10 characters.
print(thomas.is_password_valid()) # Output: True
print(peter.is_password_valid()) # Output: False
V príklade máme metódy:
validate_password()- statická metóda a je možné ju volať aj cez inštanciu. Jej logika však závisí iba od konkrétnej triedyUser, nie od toho, 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.password_info()- triedna metóda v triedeVIPUser, ktorú ak voláme cez inštanciu, vracia informácie špecifické pre triedu tej inštancie (v našom prípadeVIPUser).is_password_valid()- metóda inštancie, ktorá využíva statickú metóduvalidate_password(). Demonštruje, ako metóda inštancie dokáže využívať statické aj triedne metódy.
To je v rámci tejto lekcie 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é 39x (1.38 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python
