Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

21. diel - Hracia kocka v Pythone - Random a prekrývanie metód

V predchádzajúcom kvíze, Kvíz - Dátum a čas v Pythone, sme si overili nadobudnuté skúsenosti z predchádzajúcich lekcií.

V tomto tutoriále objektovo orientovaného programovania v Pythone budeme pokračovať v tvorbe našej objektovej hracej kocky. Začneme s metódou pre hod. Vysvetlíme si na nej bližšie vnútorný import a potom dôkladne preskúmame princípy prekrývania metód. Vrátime sa tiež k dôležitým detailom týkajúcich sa zapuzdrenia.

Náhodné čísla

Definujme na kocke metódu hod(), ktorá nám vráti náhodné číslo od 1 do počtu stien. Metóda nebude mať žiadny parameter. Náhodné číslo získame za pomoci modulu random. Modul si naimportujeme vnútorne (použijeme jedno podčiarknutie) a použijeme metódu randint():

Použitie jedného podčiarkovníka pred názvom importovaného modulu (alebo akejkoľvek inej premennej) je - nám už známou - konvenciou v Pythone, ktorá signalizuje, že táto premenná by mala byť považovaná za privátnu a nemala by byť priamo pristupovaná mimo triedy alebo modulu.

Na čo slúži vnútorný import

Výraz "vnútorný import" sa vzťahuje na prax importovania modulu priamo vo vnútri funkcie alebo metódy, namiesto toho, aby bol importovaný na začiatku súboru. V niektorých prípadoch sa to môže hodiť, najmä ak:

  • modul potrebujeme len v určitých špecifických situáciách,
  • import modulu je nákladný na pamäť a čas a nechceme, aby spomaľoval štart aplikácie.
Je dôležité si uvedomiť, že importovanie modulu v Pythone je relatívne nákladná operácia. Avšak iba prvýkrát, kedy je modul importovaný. Ďalšie pokusy o jeho importovanie sú už veľmi rýchle, pretože Python použije už načítanú verziu modulu z pamäte. Inými slovami, pri importovaní modulov sa Python pozrie, či bol už modul importovaný. Ak áno, Python ho znova neimportuje. Preto sa prvý riadok v metóde vykoná len raz. Tento koncept teda berte len ako ukážku možností jazyka. Importy na začiatku súboru, ako sme zvyknutí, sú takmer za všetkých okolností jednoduchším a lepším riešením.

Nadmerné používanie vnútorných importov kód komplikuje a znižuje jeho čitateľnosť. Dobrou praxou je teda používať ich uvážene a len v situáciách, keď prinášajú skutočné výhody.

Prekrývanie metódy __str__()

Kocka je takmer hotová. Ukážme si teraz ešte jednu užitočnú metódu, ktorú budeme hojne používať aj vo väčšine našich ďalších objektov. Reč je o metóde __str__(). Túto metódu obsahuje každý objekt, teda aj teraz naša kocka. Metóda je určená na to, aby vrátila tzv. textovú reprezentáciu inštancie. Hodí sa vo všetkých prípadoch, keď si inštanciu potrebujeme vypísať alebo s ňou pracovať ako s textom. Túto metódu majú napr. aj čísla. V Pythone funguje implicitná konverzia. Akonáhle teda budeme chcieť do konzoly vypísať číslo alebo ktorýkoľvek iný objekt, Python na ňom zavolá metódu __str__() a vypíše jej výstup. Ak si robíme vlastnú triedu, mali by sme zvážiť, či sa nám takáto metóda nehodí. Nikdy by sme si nemali robiť vlastnú metódu, napr. niečo ako vypis(), keď už máme v Pythone pripravenú cestu, ako toto riešiť. Pri kocke nemá __str__() vyšší zmysel, ale u bojovníka bude určite vracať jeho meno. My si ju ku kocke aj tak pridáme. Bude vypisovať, že sa jedná o kocku a vráti aj počet stien. Najprv si vypíšme do konzoly našu inštanciu kocky:

Do konzoly sa vypíše iba cesta k našej triede:

Hoci je metóda už definovaná a poskytuje nám určitý výstup, chceme o našej kocke získať lepšie informácie. Najlepšie také, o ktorých sami rozhodneme, že je vhodné ich zobraziť. To docielime tým, že vstavanú metódu __str__() jednoducho definujeme sami znova a tým ju prekryjeme:

V konzole uvidíme:

Ešte si naše kocky vyskúšame. Skúsime si v programe s našimi dvoma kockami v cykloch hádzať a pozrieme sa, či fungujú tak, ako sa očakáva. Upravíme koniec súboru:

Za for nasleduje podtržítko. Divné, že? V kontexte slučky for sa jednoduché podtržítko _ používa ako dočasná premenná, keď nás skutočná hodnota v každom priechode slučkou nezaujíma. Je to konvenčný spôsob, ako naznačiť, že hodnota tejto premennej nebude v danom kontexte využitá.

Máme hotovú celkom peknú a nastaviteľnú triedu, ktorá reprezentuje hraciu kocku. Bude sa nám hodiť v našej aréne, ale k dispozícii ju máme na využitie aj kdekoľvek inde. Vidíme, ako OOP umožňuje znovu používať komponenty.

Zapuzdrenie v Pythone detailne

Teraz už máme dostatok znalostí, aby sme sa dôkladne zahĺbali nad možnosťami zapuzdrenia. Povedali sme si, že aj keď nám Python umožňuje nastavovať atribúty privátne, nie sú od chtivých rúk programátora úplne oddelené.

Uveďme si príklad s našou kockou. Upravíme atribút pocet_sten na privátne pomocou dvoch podčiarkovníkov a skúsme k nemu pristúpiť v kóde, zmeniť jeho hodnotu a tú vypísať:

V konzole uvidíme výstup:

To je veľmi zaujímavé správanie, že? Zdá sa, že Python nám umožňuje bez vyvolania chyby zmeniť privátny atribút, ale na túto zmenu potom neberie ohľad. Bohužiaľ, je to trochu komplikovanejšie.

Už sme si povedali, že v Pythone neexistuje striktná privátnosť, ako ju poznáme z iných jazykov (ako je Java alebo C++). Namiesto toho Python používa konvenciu "name mangling". Keď deklarujeme atribút s dvojitým podčiarkovníkom, napr. __pocet_sten, Python tento názov automaticky zmení na _NazevTridy__pocet_sten. V prípade nášho kódu sa __pocet_sten interne stáva atribútom s názvom _Kostka__pocet_sten.

Náš pokus o priradenie novej hodnoty tomuto atribútu (kostka.__pocet_sten = 365) tak vôbec neupravil pôvodný privátny atribút __pocet_sten triedy Kostka. Vytvoril totiž atribút úplne nový. Pôvodný privátny atribút, ktorý sa vďaka dvojitému podtržítku premenoval na _Kostka__pocet_sten, zostáva nezmenený. Preto posledný výpis v našom pokuse potvrdil, že kocka je stále desaťstenná.

Prístup k privátnemu atribútu

Pokiaľ by sme naozaj chceli počet stien kocky zmeniť, museli by sme použiť tento kód:

V konzole potom uvidíme:

Prečo ide o zlú praktiku

Hlavným dôvodom, prečo sú niektoré atribúty označené ako privátne, je zapuzdrenie. Zapúzdrenie je jedným z kľúčových princípov OOP. Priamy prístup k privátnym atribútom túto koncepciu narúša. Predstavme si, že sa autor triedy neskôr rozhodne zmeniť implementáciu alebo štruktúru interného atribútu a my mu v inej časti kódu "natvrdo" meníme jeho hodnotu. Máločo je lepší spôsob, ako do kódu zaniesť neočakávané chyby. Prístup k privátnym atribútom tiež výrazne zhorší čitateľnosť kódu.

Skrátka, aj keď môžeme technicky pristupovať k privátnym atribútom cez „name mangling“, mali by sme to robiť len v naozaj výnimočných situáciách, a radšej vôbec nie. Takmer vždy je lepšie rešpektovať zapuzdrenie a pracovať s objektmi tak, ako boli navrhnuté.

Vytváranie atribútov za behu

Povedali sme si, že pokus o priame prepísanie privátneho atribútu skončil tak, že Python namiesto toho vytvoril nový atribút. V Pythone je povolená veľká miera dynamiky, a to zahŕňa schopnosť pridávať atribúty k objektom za behu. Toto je často považované za jednu z flexibilných vlastností jazyka, ale veľmi ľahko to spôsobí zmätky, pokiaľ si nie sme tejto vlastnosti vedomí.

Triedy v Pythone sú modifikovateľné za behu. Keď vytvoríme inštanciu triedy, Python nám umožňuje tejto inštancii pridať nové atribúty, aj keď neboli definované v pôvodnej triede.

Ukážme si príklad: Vo výstupe konzoly uvidíme:

Je zrejmé, že táto vlastnosť Pythona vedie do nebezpečných vôd:-) Vymazlíme si triedu k dokonalosti, len aby nám do nej kolega napchal svoje vlastné atribúty, ktoré nemusia byť úplne konzistentné s naším návrhom. Potenciálne more problémov je jasné. Našťastie pre to má Python riešenie.

Využitie atribútu __slots__

V Pythone je __slots__ špeciálny atribút triedy, ktorý definuje pevný zoznam atribútov, ktoré môže mať inštancia tejto triedy. Použitím __slots__ vylúčime možnosť vytvárať nové atribúty mimo definovaného zoznamu:

Pokus o vytvorenie atribut3 vyvolá chybu:

To je pre dnešnú lekciu všetko. Kompletný kód kocky je k dispozícii v archíve.

V budúcej lekcii, Dekorátory druhýkrát - Parametrické a triedne dekorátory , budeme v téme dekorátorov pokračovať.


 

Predchádzajúci článok
Kvíz - Dátum a čas v Pythone
Všetky články v sekcii
Objektovo orientované programovanie v Pythone
Preskočiť článok
(neodporúčame)
Dekorátory druhýkrát - Parametrické a triedne dekorátory
Článok pre vás napísal Karel Zaoral
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Karel Zaoral
Aktivity