← Přehled všech hodin

Třídy a objekty

Cíl hodiny: Chápeš, že třída je předpis a objekt je konkrétní věc, a umí napsat vlastní třídu s metodami.
Jak stránku používat: vpravo nahoře je tlačítko Prezentace — schová všechno kromě jednoho bloku a přepíná se šipkami. U ukázek a cvičení je kód vlevo editovatelný: uprav ho a klikni na Spustit (nebo Ctrl+Enter), výsledek se objeví vpravo. Běží to přímo v prohlížeči, bez instalace.

1. Vysvětlení a souvislosti

Blok 1Kam jsme došli a co chybí1. Vysvětlení a souvislosti

1.1 Kam jsme došli a co chybí

Zrekapitulujte cestu:

Nejdřív jsi měl proměnné. Pak seznam, aby jich mohlo být víc. Pak slovník, aby měly jména. Pak funkce, aby se s nimi dalo pracovat.

Ale pořád máš data zvlášť a funkce zvlášť. Musíš si pamatovat, které funkce patří ke kterým datům. To je poslední problém, který vyřešíme.

# Doteď:
hrdina = {"jmeno": "Arkon", "zivoty": 100}
uber_zivoty(hrdina, 20)         # data a funkce zvlášť

# Nově:
hrdina = Postava("Arkon", 100)
hrdina.uber_zivoty(20)          # funkce PATŘÍ k datům

Třída spojí data a to, co se s nimi dá dělat, do jednoho balíčku.

Blok 2Třída je předpis, objekt je výrobek1. Vysvětlení a souvislosti

1.2 Třída je předpis, objekt je výrobek

Toto je nejdůležitější věta hodiny. Pomůže analogie, kterou znáš:

Třída je formička na perník. Objekt je perník. Z jedné formičky uděláš stovky perníků a každý může mít jinou polevu.

Nebo: třída je stavební plán domu. Objekt je konkrétní postavený dům. Plán je jeden, domů podle něj postavíš kolik chceš.

class Postava:      # předpis
    ...

arkon = Postava("Arkon", 100)    # konkrétní objekt
bela = Postava("Bela", 80)       # jiný objekt, stejný předpis

arkon a bela jsou dvě různé postavy. Když ubereš životy jedné, ta druhá o tom neví. Každý objekt má vlastní data.

Blok 3Anatomie třídy1. Vysvětlení a souvislosti

1.3 Anatomie třídy

Část Co to je
class Postava: začátek předpisu, název velkým písmenem
__init__ konstruktor — spustí se při vytvoření objektu
self odkaz na konkrétní objekt, se kterým právě pracuješ
self.jmeno atribut — data, která si objekt nese
uber_zivoty metoda — funkce, která patří k objektu
Blok 4self — nejtěžší část hodiny1. Vysvětlení a souvislosti

1.4 self — nejtěžší část hodiny

Tohle je potřeba pochopit pomalu a klidně i dvakrát.

self znamená „tenhle konkrétní objekt". Když napíšeš arkon.uber_zivoty(20), Python zavolá metodu a jako self jí podá arkon. Kdybys volal bela.uber_zivoty(20), jako self by dostala bela.

Proto v metodě píšeš self.zivoty — říkáš tím „životy TOHOHLE objektu", ne nějaké obecné životy.

Podívej se, co se stane bez self:

def uber_zivoty(self, kolik):
    zivoty = zivoty - kolik      # UnboundLocalError - to je jiná proměnná!

Bez self. vytváříš obyčejnou lokální proměnnou, která po skončení metody zmizí. Objekt se vůbec nezmění.

Pravidlo: v každé metodě je self první parametr. Vždycky. Při volání ho ale nepíšeš — Python ho doplní sám.

Blok 5__init__ — konstruktor1. Vysvětlení a souvislosti

1.5 init — konstruktor

arkon = Postava("Arkon", 100)

Když napíšeš název třídy se závorkami, Python vytvoří nový prázdný objekt a hned na něm spustí __init__. Tam si objekt nastaví, co má mít.

Ty dvě podtržítka z každé strany znamenají „tohle je speciální jméno, které Python zná". Říká se jim dunder metody.

def __init__(self, jmeno, zivoty=100, sila=10):
    ...

Postava("Arkon")              # zivoty 100, sila 10
Postava("Bela", 80)           # zivoty 80, sila 10
Postava("Cedrik", 60, 20)     # všechno vlastní

Výchozí hodnoty fungují stejně jako u obyčejných funkcí:

Blok 6__str__ — jak se objekt vypíše1. Vysvětlení a souvislosti

1.6 str — jak se objekt vypíše

class Postava:
    ...
    def __str__(self):
        return f"{self.jmeno} ({self.zivoty} HP)"

st.write(str(arkon))        # "Arkon (85 HP)"

Bez __str__ by ti Python vypsal něco jako <__main__.Postava object at 0x104a2f10>, což je k ničemu. __str__ mu řekne, jak se má tvůj objekt představit.

Blok 7Atributy třídy versus atributy objektu1. Vysvětlení a souvislosti

1.7 Atributy třídy versus atributy objektu

Co je nahoře v těle třídy, mají všechny objekty stejné. Co je v __init__ se self., má každý objekt vlastní.

Pro začátek stačí, že konstanty (MAX_ZIVOTY) patří nahoru.

Poznámky pro učiteleObjekty ve Streamlitu — praktická poznámka1. Vysvětlení a souvislosti

1.8 Objekty ve Streamlitu — praktická poznámka

Objekt se dá uložit do session_state úplně normálně:

if "hrdina" not in st.session_state:
    st.session_state.hrdina = Postava("Arkon")

h = st.session_state.hrdina
h.uber_zivoty(10)             # změna se uchová, objekt je odkaz

Ale pozor — při každé změně souboru Streamlit modul znovu načte a třída se vytvoří znovu. Starý objekt v session_state pak patří „staré" třídě. Projeví se to divnými chybami po editaci kódu. Řešení: po větší úpravě třídy stiskněte tlačítko „Nová hra" nebo obnovte stránku.

Tohle dětem řekněte předem, ušetříte si zmatek.

Blok 8Kdy použít třídu a kdy stačí slovník1. Vysvětlení a souvislosti

1.9 Kdy použít třídu a kdy stačí slovník

Buďme poctiví:

Použij slovník Použij třídu
Jen data, žádné chování Data i chování patří k sobě
Data jdou rovnou do JSON Objekt má svá pravidla a kontroly
Jednorázová struktura Vytváříš víc podobných věcí

Třída není vždycky lepší. Ale jakmile ke stejným datům píšeš tři funkce, které je mění, je čas na třídu.

Poznámky pro učiteleTechnické pozadí pro učitele1. Vysvětlení a souvislosti

1.10 Technické pozadí pro učitele

  • Python nemá private. Konvence: _atribut znamená „interní, nesahat". Dvojité podtržítko __x spouští name mangling, ale to sem nepatří.
  • @property umožňuje počítané atributy — pěkný bonus pro pokročilé: @property def procenta(self): return self.zivoty / self.MAX * 100
  • @dataclass z modulu dataclasses výrazně zkracuje zápis datových tříd. Ukažte až pokročilým, jinak zastíní pochopení __init__.
  • Pro C# programátora: __init__ je konstruktor, self je this, ale musí být explicitně první parametr. Všechny metody jsou virtuální, vše je veřejné.
  • Serializace objektu do JSON nejde přímo — potřebuje metodu to_dict(). To využijeme v hodině 17.
Poznámky pro učiteleČasté chyby1. Vysvětlení a souvislosti

1.11 Časté chyby

Chyba Příčina Řešení
TypeError: missing 1 required positional argument: 'self' Chybí self v definici metody Přidat jako první parametr
Objekt se nezmění Chybí self. před atributem self.zivoty = ...
AttributeError: object has no attribute Atribut se nenastavil v __init__ Doplnit do konstruktoru
Všechny objekty mají stejná data Měnitelná hodnota v hlavičce třídy Vytvářet v __init__
<object at 0x...> při výpisu Chybí __str__ Doplnit metodu
Divné chyby po úpravě třídy Starý objekt v session_state Reset hry / obnovit stránku


2. Ukázky pro projekci

Blok 9Ukázka 1 — Od slovníku ke třídě2. Ukázky pro projekci
demo/demo1_slovnik_vs_trida.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Blok 10Ukázka 2 — self pod lupou2. Ukázky pro projekci
demo/demo2_self.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Blok 11Ukázka 3 — Souboj dvou objektů2. Ukázky pro projekci
demo/demo3_souboj_objekty.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.

3. Cvičení na hodinu

Blok 12Cvičení 1 — První třída (15 min)3. Cvičení na hodinu

Zadání:

  1. Napiš třídu Postava s __init__(self, jmeno, zivoty=100)
  2. Přidej metodu uber_zivoty(self, kolik), která nikdy nejde pod nulu
  3. Přidej metodu je_ziva(self) vracející True/False
  4. Vytvoř dva různé objekty a ukaž, že se navzájem neovlivňují
  5. Přidej __str__, aby se postava hezky vypsala
Úroveň Co má zvládnout
Minimum Body 1–3
Rozšíření Bod 4 se dvěma tlačítky
Výzva Bod 5 + metoda uzdrav s limitem maxima

Očekávaný výsledek: dvě nezávislé postavy ovládané tlačítky.

cviceni/cviceni1_prvni_trida.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Na co si dát pozor
  • V každé metodě má být self jako první parametr.
  • Častá chyba je práce bez self. (zivoty = ... místo self.zivoty = ...).
  • Ověř si na dvou objektech, že změna jedné postavy neovlivní druhou.
Řešení 1 — jedna z možných variant
reseni/reseni1_prvni_trida.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Blok 13Cvičení 2 — Třída s inventářem (20 min)3. Cvičení na hodinu

Zadání:

  1. Rozšiř Postava o atribut inventar (prázdný seznam v __init__)
  2. Přidej metodu seber(self, vec) přidávající do inventáře
  3. Přidej metodu ma(self, vec) vracející True/False
  4. Přidej metodu vyhod(self, vec), která bezpečně odebere věc
  5. Přidej metodu pocet_veci(self) a __str__ s výpisem inventáře
Úroveň Co má zvládnout
Minimum Body 1–3
Rozšíření Bod 4 s kontrolou existence
Výzva Bod 5 + limit velikosti batohu jako atribut třídy

Očekávaný výsledek: postava, která si sama spravuje batoh.

cviceni/cviceni2_inventar_trida.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Na co si dát pozor
  • inventar má vzniknout v __init__, ne jako sdílený atribut třídy.
  • U metody vyhod připomeňte kontrolu existence věci před odebráním.
  • Když je někdo hotový, ať si výpis inventáře ověří přes vlastní __str__.
Řešení 2 — jedna z možných variant
reseni/reseni2_inventar_trida.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Blok 14Cvičení 3 — Třída Příšera a souboj (20 min)3. Cvičení na hodinu

Zadání:

  1. Napiš třídu Prisera s jménem, životy, silou a zlatem
  2. Přidej metodu utoc(self, cil), která volá metodu cíle
  3. Přidej prijmi_poskozeni(self, kolik) do obou tříd
  4. Udělej tlačítko, které provede jedno kolo souboje
  5. Po smrti příšery přidej hrdinovi její zlato
Úroveň Co má zvládnout
Minimum Body 1–3
Rozšíření Bod 4 s logem
Výzva Bod 5 + hod kostkou k útoku

Očekávaný výsledek: dva objekty, které se navzájem ovlivňují.

cviceni/cviceni3_prisera.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Na co si dát pozor
  • Žáci často zapomenou implementovat prijmi_poskozeni v obou třídách.
  • utoc(self, cil) má volat metodu na cíli, ne měnit cíl bokem.
  • Omez životy přes max(0, ...), aby HP nešla do mínusu.
Řešení 3 — jedna z možných variant
reseni/reseni3_prisera.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Blok 15Cvičení 4 — Družina objektů (20 min)3. Cvičení na hodinu

Zadání:

  1. Vytvoř seznam tří objektů Postava s různými jmény a životy
  2. Vypiš je cyklem, každou jako kartu s st.metric a st.progress
  3. Přidej ke každé tlačítko „Zraň" s parametrem key odvozeným od jména
  4. Spočítej, kolik členů družiny je ještě naživu
  5. Přidej metodu stav(self) vracející slovní popis a použij ji v kartě
Úroveň Co má zvládnout
Minimum Body 1–2
Rozšíření Body 3–4
Výzva Bod 5 + najít cyklem nejzraněnějšího člena

Očekávaný výsledek: tři karty generované jedním kusem kódu.

cviceni/cviceni4_druzina.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Na co si dát pozor
  • Klíčové je použít cyklus přes seznam objektů místo kopírování kódu.
  • U tlačítek je nutný unikátní key, jinak se budou tlačítka plést.
  • Pro statistiku "naživu" a nejzraněnějšího ukažte průchod seznamem krok za krokem.
Řešení 4 — jedna z možných variant
reseni/reseni4_druzina.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Blok 16Cvičení 5 — PyQuest na třídách (25 min)3. Cvičení na hodinu

Zadání:

  1. Převeď hrdinu na objekt třídy Hrdina s metodami uber_zivoty, pridej_zlato, seber
  2. Přidej metodu muze_do_veze(self) vracející True/False
  3. Ulož objekt do session_state a používej ho v celé hře
  4. Přidej metodu karta(self), která vykreslí hrdinu v postranním panelu
  5. Přidej metodu vyprava(self, data) vracející text zápisu do deníku
Úroveň Co má zvládnout
Minimum Body 1–3
Rozšíření Bod 4
Výzva Bod 5 + metoda to_dict() pro pozdější ukládání

Očekávaný výsledek: hra běží na objektu, hlavní kód je krátký.

cviceni/cviceni5_pyquest_tridy.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Na co si dát pozor
  • Objekt Hrdina musí být uložený v st.session_state, jinak se při každém běhu ztratí změny.
  • Pravidlo věže (klíč) patří do metody, ne jen do hlavního těla programu.
  • Když žáci upraví třídu a objeví se podivné chyby, pomůže reset hry.
Řešení 5 — jedna z možných variant
reseni/reseni5_pyquest_tridy.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.

4. Domácí úkol

Blok 17Domácí úkol na příští hodinu4. Domácí úkol

Napiš třídu Lektvar s atributy nazev, sila_leceni a cena a metodou pouzij(self, postava), která postavě přidá životy. Vytvoř tři různé lektvary a nech hrdinu vybrat, který vypije.

Zobrazit ukázkové řešení domácího úkolu

Toto je jedna z možných variant, jak mohl úkol dopadnout. Tvoje řešení se může lišit a to je v pořádku.

domaci/domaci_ukol13.py
Tady se objeví výsledekKlikni na Spustit. Kód vlevo můžeš měnit a spouštět opakovaně klávesami Ctrl+Enter.
Kód vlevo jde upravovat. Tab odsazuje o čtyři mezery.
Blok 18Oficiální dokumentace4. Domácí úkol

Oficiální dokumentace se hodí, když si chceš ověřit, jak Python zapisuje třídy, co přesně dělá __init__ nebo proč se u metod objevuje self. Je v angličtině, ale často stačí projít názvy částí, krátké ukázky a příklady použití.

Co jsme použili Oficiální dokumentace Co tam najdeš
třídy obecně Python tutorial — Classes základní vysvětlení tříd, objektů, atributů a metod
class Class definitions přesný zápis definice třídy v Pythonu
__init__ object.__init__ konstruktor a inicializaci nového objektu
self a instance A first look at classes proč metody pracují s konkrétní instancí
metody objektu Method objects jak funguje volání metod přes tečku
__str__ object.__str__ jak si nastavit hezký textový výpis objektu

5. Umíme informatiku

Blok 19Navazující cvičení na Umíme informatiku5. Umíme informatiku
Cvičení Odkaz Proč se hodí
Kódování a modelování umimeinformatiku.cz Modelování skutečnosti
Programování v Pythonu umimeinformatiku.cz Struktura programu
Algoritmické myšlení umimeinformatiku.cz Rozklad na části
Tabulky umimeinformatiku.cz Objekty jako řádky dat