3. diel - Hracia kocka v Pythone - Zapuzdrenie a konštruktor
V predchádzajúcom cvičení, Riešené úlohy k 1.-2. lekciu OOP v Pythone, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.
V tomto tutoriáli objektovo orientovaného programovania v Pythone začneme pracovať na sľúbenej aréne, v ktorej budú proti sebe bojovať dvaja bojovníci. Boj bude ťahový (na preskáčku) a bojovník vždy druhému uberie život na základe sily jeho útoku a obrany druhého bojovníka. Simulujeme v podstate stolnú hru a budeme teda simulovať aj hraciu kocku, ktorá dodá hre prvok náhodnosti. Začnime zvoľna a vytvorme si dnes práve túto hraciu kocku. Zároveň sa naučíme ako definovať vlastný konštruktor.
Vytvorenie projektu
Vytvoríme si nový projekt a pomenujeme ho TahovyBoj
. V
projekte vytvoríme nový súbor kostka.py
av ňom triedu s názvom
Kostka
. Zamyslime sa nad atribútmi, ktoré kocke dáme. Iste by sa
hodilo, keby sme si mohli zvoliť počet stien kocky (klasicky 6 alebo 10 stien,
ako je zvykom pri tomto type hier). Naša trieda preto bude mať atribút
pocet_sten
.
Minule sme kvôli jednoduchosti nastavovali všetky atribúty našej triedy ako verejne prístupné. Väčšinou sa však skôr nechce, aby sa dali zvonku modifikovať. Preto sa nastavujú ako súkromné. Súkromné atribúty začínajú jedným alebo dvoma podčiarkovníkmi. Jedným podčiarkovníkom nie je prístup odmietnutý, ale dávame najavo, že daný prvok sa nemá z vonkajšej používať. Dve podčiarkovníky spôsobia, že k atribútu potom nemožno normálne pristupovať.
My budeme v kurze používať jedno podčiarknutie.
Pri návrhu triedy teda použijeme pre atribúty podčiarkovníka. Nepoužijeme ich iba v prípade, že niečo bude naozaj potrebné vystaviť. Naša trieda teraz vyzerá takto:
class Kostka: """ Třída reprezentuje hrací kostku. """
Konštruktory
Až doteraz sme nevedeli zvonku nastaviť iné atribúty ako verejné, pretože súkromné atribúty nie sú zvonku viditeľné. Už sme si hovorili niečo málo o konštruktore objektu. Je to metóda, ktorá sa zavolá vo chvíli vytvorenia inštancie objektu. Slúži na nastavenie vnútorného stavu objektu a na vykonanie prípadnej inicializácie. Kocku teraz vytvoríme takto:
kostka = Kostka()
Práve Kostka()
je konštruktor. Pretože v našej triede
žiadny nie je, Python si sám vygeneruje prázdnu metódu. My si však teraz
konštruktor do triedy pridáme. Deklaruje sa ako metóda. V Pythone môžeme
použiť metódy hneď dve. Metódu __new__()
a metódu
__init__()
. Tá prvá sa volá pri vytváraní objektu, ale
väčšinou si vystačíme sa druhú metódu, ktorá sa volá pri inicializácii
objektu.
Popis rozdielu medzi oboma metódami vyžaduje výrazne hlbšie znalosti
princípov OOP, než ktorými zatiaľ disponujeme. Väčšina programátorov v
Pythone nikdy nepotrebuje prepísať metódu __new__()
. Drvivá
väčšina tried potrebuje iba __init__()
na nastavenie
počiatočného stavu objektu. Pokiaľ si teda nie sme odôvodnene istí, či
potrebujeme __new__()
, tak ju nepotrebujeme
Vráťme sa teda k metóde __init__()
. Ako prvý
parameter píšeme self
. Pokiaľ v metóde
__init__()
len tak vytvoríme nejakú premennú, tak tá po
ukončení metódy zaniká. My ale potrebujeme vytvoriť atribút
pocet_sten
, ktorý chceme ďalej používať. Atribúty objektov sa
vytvárajú všemocným slovíčkom self
. Za self
nasleduje bodka a názov atribútu. Vytvoríme teda verejný atribút
pocet_sten
:
def __init__(self): self.pocet_sten = 6
Ak kocku teraz vytvoríme, bude atribút pocet_sten
nastavený
na 6
. Vypíšme si počet stien do konzoly, nech vidíme, že tam
hodnota naozaj je:
class Kostka:
"""
Třída reprezentuje hrací kostku.
"""
def __init__(self):
self.pocet_sten = 6
kostka = Kostka()
print(kostka.pocet_sten)
V konzole vidíme výstup:
Atribut pocet_sten:
6
Zapuzdrenie
Nie je dobré atribút pocet_sten
nastaviť ako verejný,
pretože nechceme, aby nám niekto mohol už pri vytvorenej kocke počet stien
meniť. Pridáme do triedy teda metódu vrat_pocet_sten()
, ktorá
nám vráti hodnotu atribútu pocet_sten
a tento atribút upravíme
na neverejný pomocou podčiarkovníka. Docielime tým v
podstate to, že je atribút označený ako read-only (atribút
by sme mali iba čítať metódou). Aby bol skutočne read-only a zvonku
neprístupný, museli by sme podčiarkovníky použiť dve, ale tým sa zatiaľ
nebudeme zaoberať. Upravená verzia triedy aj s metódou:
class Kostka:
"""
Třída reprezentuje hrací kostku.
"""
def __init__(self):
self._pocet_sten = 6 # jedno podtržítko dává najevo, že nechceme, aby se k atributu přistupovalo přímo
def vrat_pocet_sten(self):
"""
Vrátí počet stěn kostky.
"""
return self._pocet_sten
kostka = Kostka()
print(kostka.vrat_pocet_sten())
V konzole vidíme výstup:
Výstup metody vrat_pocet_sten():
6
Atribút sa stal neverejným vďaka pridaniu podčiarkovníka. Navyše sme zmenili vypisovanie, pretože hodnotu atribútu zistíme iba zavolaním metódy.
Voliteľný počet stien
Vidíme, že sa konštruktor naozaj zavolal. My by sme ale chceli, aby sme mohli pri každej kocke pri vytvorení špecifikovať, koľko stien budeme potrebovať. Dáme teda konštruktoru parameter:
def __init__(self, pocet_sten): self._pocet_sten = pocet_sten
Vidíme, že názvy atribútu a argumentu sú skoro rovnaké. Pokiaľ by sme
mali počet stien ako verejný atribút, rovnaký názov nevadí. Pomocou
self
špecifikujeme, že ľavá premenná pocet_sten
patrí inštancii, pravú Python chápe ako odovzdanú z parametra (argumentu).
S verejným atribútom by situácia vyzerala takto:
def __init__(self, pocet_sten): self.pocet_sten = pocet_sten
Vráťme sa však k pôvodnému kódu a skúsme si zadať parameter do konštruktora:
class Kostka:
"""
Třída reprezentuje hrací kostku.
"""
def __init__(self,pocet_sten):
self._pocet_sten = pocet_sten
def vrat_pocet_sten(self):
"""
Vrátí počet stěn kostky.
"""
return self._pocet_sten
kostka = Kostka(10) # v tuto chvíli se zavolá konstruktor s par. 10
print(kostka.vrat_pocet_sten())
V konzole vidíme výstup:
Výstup s parametrem konstruktoru 10:
10
Východisková hodnota kocky
Všetko funguje, ako sme očakávali. Python nám už v tejto chvíli
nevygeneruje prázdny (tzv. bezparametrický) konštruktor,
takže kocku bez parametra už vytvoriť nemožno. My to však môžeme
umožniť pomocou uvedenia východiskovej hodnoty argumentu
pocet_sten
v definícii konštruktora. Nastavíme ju na hodnotu
6
. Takúto hodnotu užívateľ našej triedy u kocky očakáva ako
východiskovú:
def __init__(self, pocet_sten=6): self._pocet_sten = pocet_sten
Vytvorme teraz dve inštancie kocky, jednu bez udania počtu stien a jednu s ním:
class Kostka:
"""
Třída reprezentuje hrací kostku.
"""
def __init__(self,pocet_sten=6):
self._pocet_sten = pocet_sten
def vrat_pocet_sten(self):
"""
Vrátí počet stěn kostky.
"""
return self._pocet_sten
sestistenna = Kostka()
desetistenna = Kostka(10) # nebo můžeme zapsat Kostka(pocet_sten=10)
print(sestistenna.vrat_pocet_sten())
print(desetistenna.vrat_pocet_sten())
Máme teda konštruktor, ktorý nám umožňuje tvoriť rôzne hracie kocky. Vďaka kľúčovému argumentu nemusíme zadávať počet stien.
To môžeme využívať aj pri všetkých ďalších metódach,
nielen pri konštruktoroch. Veľa funkcií a metód v Pythone má kľúčové
argumenty, napríklad vstavaná funkcia print()
. Je dobré si pri
metódach prejsť ich kľúčové argumenty, aby sme neprogramovali niečo, čo
už niekto urobil pred nami.
To je pre dnešnú lekciu všetko.
V budúcej lekcii, Hracia kocka v Pythone druhýkrát - Prekrývanie metód a random , sa naučíme prekrývať metódy, používať vnútorný import a dokončíme hraciu kocku.