Vianoce v ITnetwork sú tu! Dobí si teraz kredity a získaj až 80 % extra kreditov na e-learningové kurzy ZADARMO. Zisti viac.
Hľadáme nové posily do ITnetwork tímu. Pozri sa na voľné pozície a pridaj sa k najagilnejšej firme na trhu - Viac informácií.

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áli 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 na statiku - Objektovo orientované programovanie v PythonePOZOR! 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. static.py) a urobme si jednoduchú triedu User:

class User:

    def __init__(self, name, password):
        self._name = name
        self._password = password
        self._logged_in = False

    def log_in(self, entered_name, entered_password):
        if self._name == entered_name and self._password == entered_password:
            self._logged_in = True
            return True
        else:
            self._logged_in = False
            return False  # name or password is wrong

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 log_in(). 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 User, pretože k nemu logicky patrí. Ako to teda vyriešime? Údaj uložíme priamo v triede User do triedneho atribútu. Vytvoríme si k tomu atribút minimal_password_length:

class User:

    minimal_password_length = 6

    def __init__(self, name, password):
    ...
    def log_in(self, entered_name, entered_password):
    ...

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 ClassName.attribute_name:

print(User.minimal_password_length)  # We notice the capital letter in the class name – this is indeed not an instance

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ž:

new_user = User("Thomas White", "passwordissword")
print(new_user.minimal_password_length)            # the lowercase "n" in new_user indicates that we are indeed working with an instance

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 MyClass:
    class_attribute = 'This is class attribute data, available anytime without creating an instance.'

instance = MyClass()

print(instance.class_attribute)
instance.class_attribute = 'Here we change the class attribute value in the instance.'
print(instance.class_attribute)
print(MyClass.class_attribute)

Vo výstupe konzoly uvidíme:

Class attribute:
This is class attribute data, available anytime without creating an instance.
Here we change the class attribute value in the instance.
This is class attribute data, available anytime without creating an 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 User statický (= triedny) atribút next_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 next_id. Poďme si to vyskúšať:

class User:
    minimal_password_length = 6   # class attribute
    next_id = 1                   # class attribute

    def __init__(self, name, password):
        self._name = name
        self._password = password
        self._logged_in = False
        self._id = User.next_id   # assign the current id
        User.next_id += 1         # prepare the id for the next instance

admin_user = User("Thomas Admin", "adminpassword")
regular_user = User("Thomas User", "userpassword")

print(f"User ID of {admin_user._name} is {admin_user._id}")
print(f"User ID of {regular_user._name} is {regular_user._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 MyClass:
    pass

# some program code
# we realized we could use a class attribute, so let's create one:
MyClass.new_class_attribute = "This is a new class attribute!"

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 Person:
    age = 30

print(f"Original age: {Person.age} (type {type(Person.age).__name__})")

# Now we change the type of the class attribute 'age' from a number to a string
Person.age = "Thirty years"

print(f"After type change: {Person.age} (type {type(Person.age).__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é 0x (867 B)
Aplikácia je vrátane zdrojových kódov v jazyku Python

 

Predchádzajúci článok
Riešené úlohy k 8.-11. lekciu OOP v Pythone
Všetky články v sekcii
Objektovo orientované programovanie v Pythone
Preskočiť článok
(neodporúčame)
Statika v Pythone druhýkrát - Statické a triedne metódy
Článok pre vás napísal gcx11
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
(^_^)
Aktivity