Lekce 3 - Hrací kostka v Pythonu - Zapouzdření a konstruktor
V předešlém cvičení, Řešené úlohy k 1.-2. lekci OOP v Pythonu, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
V tomto tutoriálu objektově orientovaného programování v Pythonu začneme pracovat na slíbené aréně, ve které budou proti sobě bojovat dva bojovníci. Boj bude tahový (na přeskáčku) a bojovník vždy druhému ubere život na základě síly jeho útoku a obrany druhého bojovníka. Simulujeme v podstatě stolní hru a budeme tedy simulovat i hrací kostku, která dodá hře prvek náhodnosti. Začněme zvolna a vytvořme si dnes právě tuto hrací kostku. Zároveň se naučíme jak definovat vlastní konstruktor.
Vytvoření projektu
Vytvoříme si nový projekt a pojmenujeme ho TahovyBoj
. V
projektu vytvoříme nový soubor kostka.py
a v něm třídu s
názvem Kostka
. Zamysleme se nad atributy, které kostce dáme.
Jistě by se hodilo, kdybychom si mohli zvolit počet stěn kostky (klasicky 6
nebo 10 stěn, jak je zvykem u tohoto typu her). Naše třída proto bude mít
atribut pocet_sten
.
Minule jsme kvůli jednoduchosti nastavovali všechny atributy naší třídy jako veřejně přístupné. Většinou se však spíše nechce, aby se daly zvenčí modifikovat. Proto se nastavují jako soukromé. Soukromé atributy začínají jedním nebo dvěma podtržítky. Jedním podtržítkem není přístup odepřen, ale dáváme najevo, že daný prvek se nemá z vnější používat. Dvě podtržítka způsobí, že k atributu poté nelze normálně přistupovat.
My budeme v kurzu používat jedno podtržítko.
Při návrhu třídy tedy použijeme pro atributy podtržítka. Nepoužijeme je pouze v případě, že něco bude opravdu potřeba vystavit. Naše třída nyní vypadá takto:
class Kostka: """ Třída reprezentuje hrací kostku. """
Konstruktory
Až doposud jsme neuměli zvenčí nastavit jiné atributy než veřejné, protože soukromé atributy nejsou zvenčí viditelné. Již jsme si říkali něco málo o konstruktoru objektu. Je to metoda, která se zavolá ve chvíli vytvoření instance objektu. Slouží k nastavení vnitřního stavu objektu a k provedení případné inicializace. Kostku nyní vytvoříme takto:
kostka = Kostka()
Právě Kostka()
je konstruktor. Protože v naší třídě
žádný není, Python si sám vygeneruje prázdnou metodu. My si však nyní
konstruktor do třídy přidáme. Deklaruje se jako metoda. V Pythonu můžeme
použít metody hned dvě. Metodu __new__()
a metodu
__init__()
. Ta první se volá při vytváření objektu, ale
většinou si vystačíme se druhou metodu, která se volá při inicializaci
objektu.
Popis rozdílu mezi oběma metodami vyžaduje výrazně hlubší znalosti
principů OOP, než kterými zatím disponujeme. Většina programátorů v
Pythonu nikdy nepotřebuje přepsat metodu __new__()
. Drtivá
většina tříd potřebuje pouze __init__()
k nastavení
počátečního stavu objektu. Pokud si tedy nejsme odůvodněně jistí, zda
potřebujeme __new__()
, tak ji nepotřebujeme
Vraťme se tedy k metodě __init__()
. Jako první
parametr píšeme self
. Pokud v metodě
__init__()
jen tak vytvoříme nějakou proměnnou, tak ta po
ukončení metody zaniká. My ale potřebujeme vytvořit atribut
pocet_sten
, který chceme dále používat. Atributy objektů se
vytvářejí všemocným slůvkem self
. Za self
následuje tečka a název atributu. Vytvoříme tedy veřejný atribut
pocet_sten
:
def __init__(self): self.pocet_sten = 6
Pokud kostku nyní vytvoříme, bude atribut pocet_sten
nastaven
na 6
. Vypišme si počet stěn do konzole, ať vidíme, že tam
hodnota opravdu je:
class Kostka:
"""
Třída reprezentuje hrací kostku.
"""
def __init__(self):
self.pocet_sten = 6
kostka = Kostka()
print(kostka.pocet_sten)
V konzoli vidíme výstup:
Atribut pocet_sten:
6
Zapouzdření
Není dobré atribut pocet_sten
nastavit jako veřejný,
protože nechceme, aby nám někdo mohl již u vytvořené kostky počet stěn
měnit. Přidáme do třídy tedy metodu vrat_pocet_sten()
, která
nám vrátí hodnotu atributu pocet_sten
a tento atribut upravíme
na neveřejný pomocí podtržítka. Docílíme tím v
podstatě toho, že je atribut označený jako read-only
(atribut bychom měli pouze číst metodou). Aby byl skutečně read-only a
zvenčí nepřístupný, museli bychom podtržítka použít dvě, ale tím se
zatím nebudeme zabývat. Upravená verze třídy i s metodou:
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 konzoli vidíme výstup:
Výstup metody vrat_pocet_sten():
6
Atribut se stal neveřejným díky přidání podtržítka. Navíc jsme změnili vypisování, jelikož hodnotu atributu zjistíme pouze zavoláním metody.
Volitelný počet stěn
Vidíme, že se konstruktor opravdu zavolal. My bychom ale chtěli, abychom mohli u každé kostky při vytvoření specifikovat, kolik stěn budeme potřebovat. Dáme tedy konstruktoru parametr:
def __init__(self, pocet_sten): self._pocet_sten = pocet_sten
Vidíme, že názvy atributu a argumentu jsou skoro stejné. Pokud bychom
měli počet stěn jako veřejný atribut, stejný název nevadí. Pomocí
self
specifikujeme, že levá proměnná pocet_sten
náleží instanci, pravou Python chápe jako předanou z parametru (argumentu).
S veřejným atributem by situace vypadala takto:
def __init__(self, pocet_sten): self.pocet_sten = pocet_sten
Vraťme se však k původnímu kódu a zkusme si zadat parametr do konstruktoru:
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 konzoli vidíme výstup:
Výstup s parametrem konstruktoru 10:
10
Výchozí hodnota kostky
Vše funguje, jak jsme očekávali. Python nám již v tuto chvíli
nevygeneruje prázdný (tzv. bezparametrický) konstruktor,
takže kostku bez parametru již vytvořit nelze. My to však můžeme umožnit
pomocí uvedení výchozí hodnoty argumentu
pocet_sten
v definici konstruktoru. Nastavíme ji na hodnotu
6
. Takovou hodnotu uživatel naší třídy u kostky očekává
jako výchozí:
def __init__(self, pocet_sten=6): self._pocet_sten = pocet_sten
Vytvořme teď dvě instance kostky, jednu bez udání počtu stěn 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 tedy konstruktor, který nám umožňuje tvořit různé hrací kostky. Díky klíčovému argumentu nemusíme zadávat počet stěn.
Toho můžeme využívat i u všech dalších metod, nejen u
konstruktorů. Mnoho funkcí a metod v Pythonu má klíčové argumenty,
například vestavěná funkce print()
. Je dobré si u metod
projít jejich klíčové argumenty, abychom neprogramovali něco, co již
někdo udělal před námi.
To je pro dnešní lekci vše.
V příští lekci, Hrací kostka v Pythonu podruhé - Překrývání metod a random, se naučíme překrývat metody, používat vnitřní import a dokončíme hrací kostku.
Měl jsi s čímkoli problém? Zdrojový kód vzorové aplikace je ke stažení každých pár lekcí. Zatím pokračuj dál, a pak si svou aplikaci porovnej se vzorem a snadno oprav.