class, __init__, self, atributy, metody, __str__, více instancíPostava a PriseraZrekapitulujte 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.
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
arkonabelajsou dvě různé postavy. Když ubereš životy jedné, ta druhá o tom neví. Každý objekt má vlastní data.
| Čá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 |
Tohle je potřeba pochopit pomalu a klidně i dvakrát.
selfznamená „tenhle konkrétní objekt". Když napíšešarkon.uber_zivoty(20), Python zavolá metodu a jakoselfjí podáarkon. Kdybys volalbela.uber_zivoty(20), jakoselfby dostalabela.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.
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í:
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.
Co je nahoře v těle třídy, mají všechny objekty stejné. Co je v
__init__seself., má každý objekt vlastní.
Pro začátek stačí, že konstanty (MAX_ZIVOTY) patří nahoru.
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.
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.
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__.__init__ je konstruktor, self je this, ale musí být
explicitně první parametr. Všechny metody jsou virtuální, vše je veřejné.to_dict().
To využijeme v hodině 17.| 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 |
Zadání:
- Napiš třídu
Postavas__init__(self, jmeno, zivoty=100)- Přidej metodu
uber_zivoty(self, kolik), která nikdy nejde pod nulu- Přidej metodu
je_ziva(self)vracejícíTrue/False- Vytvoř dva různé objekty a ukaž, že se navzájem neovlivňují
- 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.
self jako první parametr.self. (zivoty = ... místo self.zivoty = ...).Zadání:
- Rozšiř
Postavao atributinventar(prázdný seznam v__init__)- Přidej metodu
seber(self, vec)přidávající do inventáře- Přidej metodu
ma(self, vec)vracejícíTrue/False- Přidej metodu
vyhod(self, vec), která bezpečně odebere věc- 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.
inventar má vzniknout v __init__, ne jako sdílený atribut třídy.vyhod připomeňte kontrolu existence věci před odebráním.__str__.Zadání:
- Napiš třídu
Priseras jménem, životy, silou a zlatem- Přidej metodu
utoc(self, cil), která volá metodu cíle- Přidej
prijmi_poskozeni(self, kolik)do obou tříd- Udělej tlačítko, které provede jedno kolo souboje
- 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í.
prijmi_poskozeni v obou třídách.utoc(self, cil) má volat metodu na cíli, ne měnit cíl bokem.max(0, ...), aby HP nešla do mínusu.Zadání:
- Vytvoř seznam tří objektů
Postavas různými jmény a životy- Vypiš je cyklem, každou jako kartu s
st.metricast.progress- Přidej ke každé tlačítko „Zraň" s parametrem
keyodvozeným od jména- Spočítej, kolik členů družiny je ještě naživu
- 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.
key, jinak se budou tlačítka plést.Zadání:
- Převeď hrdinu na objekt třídy
Hrdinas metodamiuber_zivoty,pridej_zlato,seber- Přidej metodu
muze_do_veze(self)vracejícíTrue/False- Ulož objekt do
session_statea používej ho v celé hře- Přidej metodu
karta(self), která vykreslí hrdinu v postranním panelu- 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ý.
Hrdina musí být uložený v st.session_state, jinak se při každém běhu ztratí změny.Napiš třídu
Lektvars atributynazev,sila_leceniacenaa metodoupouzij(self, postava), která postavě přidá životy. Vytvoř tři různé lektvary a nech hrdinu vybrat, který vypije.
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.
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 |
| 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 |