NOVINKA: Získej 40 hodin praktických dovedností s AI – ZDARMA ke každému akreditovanému kurzu!
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í.

Lekce 4 - Hrací kostka v Pythonu - Zapouzdření a konstruktor

V předešlém cvičení, Řešené úlohy k 1.-3. lekci OOP v Pythonu, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

V dnešním tutoriálu 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, 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.

Základní pilíře OOP

OOP stojí na základních třech pilířích:

  • Zapouzdření
  • Dědičnost
  • Polymorfismus.

Dnes použijeme první z nich.

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. Naše třída nyní vypadá takto:

class Kostka:
    """
    Třída reprezentuje hrací kostku.
    """

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. Jelikož jeho hodnotu budeme chtít nechat programátora vždy zadat, neuvedeme ji spolu s atributem v prostoru třídy jako minule, ale atribut vytvoříme pomocí tzv. konstruktoru.

Konstruktory

Konstruktor je metoda, která se sama 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 :-)

Přidáme tedy do třídy metodu __init__() a v ní atribut pocet_sten vytvoříme a nastavíme mu hodnotu:

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

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 parametru jsou stejné. Rozlišíme je od sebe tak, že co je psané s self. je atribut třídy a parametr metody je bez self.

Vraťme se však k původnímu kódu a zkusme si zadat parametr do konstruktoru:

kostka = Kostka(10) # v tuto chvíli se zavolá konstruktor s par. 10
print(kostka.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 parametru 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:

sestistenna = Kostka()
desetistenna = Kostka(10) # nebo můžeme zapsat Kostka(pocet_sten=10)
print(sestistenna.pocet_sten)
print(desetistenna.pocet_sten)

Máme tedy konstruktor, který nám umožňuje tvořit různé hrací kostky. Díky klíčovému parametru (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é parametry, například vestavěná funkce print(). Je dobré si u metod projít jejich klíčové parametry, abychom neprogramovali něco, co již někdo udělal před námi.

Zapouzdření

Zapouzdření umožňuje skrýt některé metody a atributy tak, aby zůstaly použitelné jen pro třídu zevnitř. Objekt si můžeme představit jako černou skřínku (anglicky blackbox), která má určité rozhraní (interface), přes které jí předáváme instrukce/data a ona je zpracovává.

Nevíme, jak to uvnitř funguje, ale víme, jak se navenek chová a používá. Nemůžeme tedy způsobit nějakou chybu, protože využíváme a vidíme jen to, co tvůrce třídy zpřístupnil.

Příkladem může být třída Clovek, která bude mít atribut datum_narozeni a na jeho základě další atributy: plnolety a vek. Kdyby někdo objektu zvenčí změnil datum_narozeni, přestaly by platit proměnné plnolety a vek. Říkáme, že vnitřní stav objektu by byl nekonzistentní. Toto se nám ve strukturovaném programování může klidně stát. V OOP však objekt zapouzdříme. Atribut datum_narozeni označíme jako privátní a tím pádem bude jasné, že nechceme, aby nám jej někdo jen tak měnil. Naopak ven vystavíme metodu zmen_datum_narozeni(), která dosadí nové datum narození do proměnné datum_narozeni a zároveň provede potřebný přepočet věku a přehodnocení plnoletosti. Použití objektu je bezpečné a aplikace stabilní.

Zapouzdření tedy tlačí programátory používat objekt jen tím správným způsobem.

Zapouzdření atributu pocet_sten

Minule jsme kvůli jednoduchosti nastavili atribut naší třídy jako veřejný přístupný. Většinou se však spíše nechce, aby je někdo zvenčí modifikoval. 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.

Atribut tedy přejmenujeme na _pocet_sten, protože nechceme, aby nám někdo již u vytvořené kostky počet stěn měnil. Chceme zároveň však vystavit možnost hodnotu jen přečíst, proto přidáme do třídy metodu vrat_pocet_sten(), která nám vrátí hodnotu atributu _pocet_sten. Docílíme tím v podstatě toho, že je atribut označený jako read-only (atribut bychom měli pouze číst metodou). Upravená verze třídy i s metodou:

class Kostka:
    """
    Třída reprezentuje hrací kostku.
    """

    def __init__(self,pocet_sten=6):
        self._pocet_sten = pocet_sten # 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.

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.

Předchozí článek
Řešené úlohy k 1.-3. lekci OOP v Pythonu
Všechny články v sekci
Objektově orientované programování v Pythonu
Přeskočit článek
(nedoporučujeme)
Hrací kostka v Pythonu podruhé - Překrývání metod a random
Článek pro vás napsal gcx11
Avatar
Uživatelské hodnocení:
827 hlasů
(^_^)
Aktivity