Skip to content

Commit bab2894

Browse files
authored
Rework lessons on class & inheritance
This upsates the lesson on classes and inheritance. The changes aren't as drastic as in some of my recent PRs :) I did remove the Cat exercise; IMO it's better to give this as an assignment. pyvec#640
2 parents 66a0156 + b9dcb47 commit bab2894

File tree

2 files changed

+117
-143
lines changed

2 files changed

+117
-143
lines changed

lessons/beginners/class/index.md

Lines changed: 86 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Objekty spojují data a funkčnost dohromady.
3232
> Funkce `len` ale funguje i na
3333
> objektech, které s řetězci nemají nic společného.
3434
35+
3536
# Třídy
3637

3738
Data každého objektu jsou specifická pro konkrétní
@@ -57,8 +58,8 @@ Tohle společné chování určuje
5758
*typ* (angl. *type*) neboli *třída* (angl. *class*) daného objektu.
5859

5960
> [note]
60-
> V historických verzích Pythonu byl rozdíl mezi „typem“
61-
> a „třídou“, ale dnes už jsou to synonyma.
61+
> V jiných jazycích než Python 3 můžou slova „typ“ a „třída“ označovat různé
62+
> věci. Pro nás to budou synonyma.
6263
6364
Typ objektu umí zjistit funkce `type`:
6465

@@ -75,9 +76,14 @@ Typ objektu umí zjistit funkce `type`:
7576
<class '_io.TextIOWrapper'>
7677
```
7778

78-
Takže `type` vrací nějaké třídy.
79-
A co je to třída? Popis, jak se chovají všechny objekty
80-
daného typu.
79+
A co je to třída? Třída je *popis*, jak se všechny objekty
80+
daného typu chovají.
81+
82+
Například `<class 'int'>` obsahuje všechno, co je společné všem celým číslům:
83+
že (a jak) se dají sčítat, jak takové číslo převést na řetězec, a tak dále.
84+
85+
86+
## Tvoření objektů třídy
8187

8288
Většinu tříd jde navíc v Pythonu zavolat, jako by
8389
to byly funkce, a vytvořit tak nový objekt dané třídy:
@@ -95,7 +101,7 @@ Chová se to stejně jako funkce `str`! Není to podivné?
95101
Tady se musím omluvit:
96102
[materiály k funkcím](../functions/)
97103
tak trochu lhaly. Funkce `str`, `int`, `float` apod. totiž vůbec
98-
nejsou funkce – jsou to právě třídy.
104+
nejsou funkce – jsou to právě třídy:
99105

100106
```pycon
101107
>>> str
@@ -108,8 +114,7 @@ True
108114

109115
Ale dají se, podobně jako funkce, zavolat.
110116
Třída tedy většinou obsahuje nejen „popis“, jak se
111-
objekty daného typu budou chovat, ale umí i objekty
112-
daného typu vytvořit.
117+
její objekty budou chovat, ale „umí“ takové objekty i vytvořit.
113118

114119
## Vlastní třídy
115120

@@ -133,8 +138,7 @@ class Kotatko:
133138

134139
Tak jako se funkce definují pomocí `def`,
135140
třídy mají klíčové slovo `class`,
136-
za které napíšeš jméno třídy, dvojtečku,
137-
a pak odsazené tělo třídy.
141+
za které napíšeš jméno třídy, dvojtečku a pak odsazené tělo třídy.
138142
Podobně jako `def` dělá funkce, příkaz
139143
`class` udělá novou třídu a přiřadí ji
140144
do proměnné daného jména (tady `Kotatko`).
@@ -148,8 +152,8 @@ aby se nepletly s „normálními“ hodnotami.
148152
> důvodů – původně to byly opravdu funkce.
149153
150154
V těle třídy můžeš definovat metody, které vypadají
151-
úplně jako funkce – jen mají první argument `self`.
152-
Ten si ale vysvětlíme později – zamňoukání má přednost:
155+
úplně jako funkce – jen mají první parametr `self`.
156+
Ten si ale vysvětlíme později – napřed zkus zamňoukat:
153157

154158
```python
155159
# Vytvoření konkrétního objektu
@@ -161,22 +165,28 @@ kotatko.zamnoukej()
161165

162166
V tomhle příkladu si dej pozor na velikost písmen:
163167
`Kotatko` (s velkým K) je třída – popis, jak
164-
se koťátka chovají. `kotatko` (s malým k)
168+
se koťátka chovají.
169+
`kotatko` (s malým k)
165170
je konkrétní objekt (angl. *instance*) té třídy:
166171
hodnota, která reprezentuje kotě.
167-
Onen konkrétní objekt vytvoříme zavoláním třídy,
168-
stejně jako zavoláním `str()` se dá vytvořit
169-
konkrétní řetězec.
172+
173+
Když definuješ třídu (pomocí bloku `class`), neznamená to zatím, že ve tvém
174+
programu je nějaké koťátko.
175+
Třída je jako recept nebo manuál: když si koupíš kuchařku, budeš teoreticky
176+
vědět jak upéct dort, jak bude takový dort vypadat a že se dá sníst.
177+
Ale neznamená to ještě, že máš samotný dort!
178+
179+
Konkrétní objekt vytvoříš až zavoláním třídy.
180+
Stejně jako zavoláním `str()` se dá vytvořit konkrétní řetězec,
181+
volání `Kotatko()` vytvoří nový objekt tvé třídy, který už můžeš použít.
170182

171183
Mňau!
172184

173185
## Atributy
174186

175-
Objekty vytvořené z „vlastních“ tříd mají jednu
176-
vlastnost, kterou třídy jako `str`
177-
nedovolují: možnost si definovat vlastní
178-
*atributy* – informace, které se uloží k danému
179-
objektu.
187+
Objekty vytvořené z „vlastních“ tříd mají funkčnost, kterou třídy jako `str`
188+
nedovolují: máš možnost si definovat vlastní
189+
*atributy* – informace, které se uloží k danému objektu.
180190
Atributy se označují tak, že mezi hodnotu a jméno
181191
jejího atributu napíšeš tečku:
182192

@@ -193,9 +203,9 @@ print(micka.jmeno)
193203

194204
Na začátku jsme si řekl{{gnd('i', 'y', both='i')}}, že objekty spojují chování
195205
a data.
196-
Chování je definováno ve třídě, data se schovávají
197-
právě v atributech jednotlivých objektů.
198-
Podle atributů jako jméno můžeš jednotlivá koťátka
206+
Chování je definováno ve třídě; data se schovávají
207+
právě v atributech jednotlivých objektů.
208+
Podle atributů jako `jmeno` pak můžeš jednotlivá koťátka
199209
rozlišit.
200210

201211
> [note]
@@ -213,18 +223,17 @@ rozlišit.
213223
214224
## Parametr `self`
215225
216-
A teď se na chvíli vrátíme k metodám,
217-
konkrétně k parametru `self`.
226+
Teď se na chvíli vraťme k metodám. Konkrétně k parametru `self`.
218227
219228
Každá metoda má přístup ke konkrétnímu objektu, na
220229
kterém pracuje, právě přes argument `self`.
221-
Teď, když máš koťátka pojmenovaná,
222-
můžeš pomocí `self` rozjet dialog:
230+
Teď, když máš koťátka pojmenovaná, můžeš v metodě `zamnoukej` použít `self`
231+
a dostat se tak ke jménu daného koťátka:
223232
224233
```python
225234
class Kotatko:
226235
def zamnoukej(self):
227-
print("{}: Mňau!".format(self.jmeno))
236+
print(f"{self.jmeno}: Mňau!")
228237
229238
mourek = Kotatko()
230239
mourek.jmeno = 'Mourek'
@@ -236,32 +245,27 @@ mourek.zamnoukej()
236245
micka.zamnoukej()
237246
```
238247
239-
Co se stalo? Výraz `mourek.zamnoukej` udělá *metodu*, která, když ji zavoláš,
240-
předá objekt `mourek` jako první argument
241-
funkce `zamnoukej`.
248+
Co se stalo? Výraz `mourek.zamnoukej` udělá *metodu*.
249+
Když ji pak zavoláš (`mourek.zamnoukej()`),
250+
objekt `mourek` se předá funkci `zamnoukej` jako první argument, `self`.
242251

243252
> [note]
244-
> Tohle je to, čím se *metoda* liší od normální *funkce*:
245-
> metoda si „pamatuje“ objekt, na kterém pracuje.
246-
247-
A takový první argument, který obsahuje konkrétní
248-
objekt právě definované třídy, se tradičně pojmenovává `self`.
249-
(Když ho pojmenuješ jinak, ostatní programátoři se na tebe budou koukat hodně
250-
divně.)
253+
> Onen první argument metody můžeš teoreticky pojmenovat i jinak než `self`,
254+
> ale když to uděláš, ostatní programátoři se na tebe budou koukat hodně divně.
251255
252256

253-
A může taková metoda brát víc než jeden argument?
257+
Může taková metoda brát víc než jeden argument?
254258
Může – `self` se doplní na první místo,
255-
a zbytek argumentů se vezme z volání metody.
259+
zbytek argumentů se vezme z volání metody.
256260
Třeba:
257261

258262
```python
259263
class Kotatko:
260264
def zamnoukej(self):
261-
print("{}: Mňau!".format(self.jmeno))
265+
print(f"{self.jmeno}: Mňau!")
262266

263267
def snez(self, jidlo):
264-
print("{}: Mňau mňau! {} mi chutná!".format(self.jmeno, jidlo))
268+
print(f"{self.jmeno}: Mňau mňau! {jidlo} mi chutná!")
265269

266270
mourek = Kotatko()
267271
mourek.jmeno = 'Mourek'
@@ -270,48 +274,64 @@ mourek.snez('ryba')
270274

271275
## Metoda `__init__`
272276

273-
A když jsme u argumentů, je ještě jedno místo,
274-
kde můžeš třídě poslat argumenty: když vytváříš
275-
nový objekt (voláním třídy).
276-
Dá se tak hezky vyřešit problém, který možná vidíš
277-
v předchozím kódu: aktuálně každé koťátko potřebuje,
278-
aby se mu po vytvoření nastavilo jméno, jinak
279-
metoda `zamnoukej` nebude fungovat.
280-
Třída se ale dá udělat i tak, že půjde jméno nastavit
281-
už při vytváření, takhle:
277+
Co se stane, když koťátku zapomeneš nastavit jméno?
278+
Metoda `zamnoukej` přestane fungovat:
279+
280+
```pycon
281+
>>> micka = Kotatko()
282+
>>> micka.snez('ryba')
283+
Traceback (most recent call last):
284+
File "<zvirata.py>", line 5, in snez
285+
AttributeError: 'Kotatko' object has no attribute 'jmeno'
286+
```
287+
288+
Aby tahle chyba nemohla nastat, můžeš zařídit, aby každé kotě *muselo* být
289+
pojmenované – a to už od okamžiku kdy vznikne.
290+
Jméno pak budeš zadávat už při vytváření kotěte, nějak takhle:
282291

283292
```python
284293
mourek = Kotatko(jmeno='Mourek')
285294
```
286295

287-
Na tohle používá Python metodu,
288-
která se jmenuje `__init__` (dvě podtržítka,
296+
To ale zatím nefunguje; musíš na to třídu `Kotatko` připravit.
297+
298+
Použij na to speciální metodu, která se jmenuje `__init__` (dvě podtržítka,
289299
`init`, dvě podtržítka).
290300
To „opodtržítkování“ znamená, že tohle jméno je nějakým způsobem speciální.
291-
Metoda `__init__` se totiž zavolá
292-
automaticky, když se vytvoří nový objekt.
293-
Dá se tedy napsat:
301+
Metodu `__init__` totiž Python zavolá
302+
automaticky, když vytvoří nový objekt.
303+
Můžeš tedy napsat:
294304

295305
```python
296306
class Kotatko:
297307
def __init__(self, jmeno):
298308
self.jmeno = jmeno
299309

300310
def zamnoukej(self):
301-
print("{}: Mňau!".format(self.jmeno))
311+
print(f"{self.jmeno}: Mňau!")
302312

303313
def snez(self, jidlo):
304-
print("{}: Mňau mňau! {} mi chutná!".format(self.jmeno, jidlo))
314+
print(f"{self.jmeno}: Mňau mňau! {jidlo} mi chutná!")
305315

306316
mourek = Kotatko('Mourek')
307317
mourek.zamnoukej()
308318
```
309319

310-
A teď už není možnost, jak vytvořit koťátko bez jména,
311-
takže `zamnoukej` bude vždycky fungovat.
320+
A teď už není možnost, jak vytvořit koťátko beze jména.
321+
Metoda `zamnoukej` bude vždycky fungovat.
322+
323+
Jako u jiných funkcí je možné jméno koťátka zadat buď jako pojmenovaný
324+
argument, nebo jako poziční. Obojí funguje stejně:
325+
326+
```
327+
mourek = Kotatko('Mourek') # 'Mourek' je hodnota prvního argument pro __init__ (po self)
328+
micka = Kotatko(jmeno='Micka') # 'Micka' je hodnota argumentu `jmeno`
329+
```
330+
331+
### Metoda `__str__`
312332

313-
Podobných „opodtržítkovaných“ metod je víc,
314-
třeba `__str__` se volá, když je potřeba
333+
Podobných „opodtržítkovaných“ (speciálních) metod je víc.
334+
Třeba metodu `__str__` Python zavolá, když je potřeba
315335
převést objekt na řetězec:
316336

317337
```python
@@ -320,60 +340,18 @@ class Kotatko:
320340
self.jmeno = jmeno
321341

322342
def __str__(self):
323-
return '<Kotatko jmenem {}>'.format(self.jmeno)
343+
return f'<Kotatko jmenem {self.jmeno}>'
324344

325345
def zamnoukej(self):
326-
print("{}: Mňau!".format(self.jmeno))
346+
print(f"{self.jmeno}: Mňau!")
327347

328348
def snez(self, jidlo):
329-
print("{}: Mňau mňau! {} mi chutná!".format(self.jmeno, jidlo))
349+
print(f"{self.jmeno}: Mňau mňau! {jidlo} mi chutná!")
330350

331351
mourek = Kotatko('Mourek')
332352
print(mourek)
333353
```
334354

335-
## Cvičení: Kočka
336-
337-
Teď, když už umíš dělat koťátka, zkus vytvořit třídu pro kočku.
338-
339-
- Kočka umí mňoukat metodou `zamnoukej`.
340-
- Kočka má na začátku (při vytvoření) 9 životů
341-
(nemůže mít nikdy víc než 9 nebo míň než 0!).
342-
- Kočka umí říct, jestli je živá (nemá 0 životů), metodou `je_ziva`.
343-
- Kočka může ztratit život metodou `uber_zivot`.
344-
- Kočku můžeš nakrmit metodou `snez`, která bere 1 argument -
345-
nějaké konkrétní jídlo (řetězec). Pokud je toto jídlo `"ryba"`, kočce se obnoví
346-
jeden život (pokud teda už není mrtvá, nebo nemá maximální počet životů).
347-
348-
{% filter solution %}
349-
```python
350-
class Kocka:
351-
def __init__(self): # Init funkce nemusi brat jako parametr
352-
self.pocet_zivotu = 9 # pocet zivotu, ten je pokazde 9.
353-
354-
def zamnoukej(self):
355-
print("Mnau, mnau, mnauuu!")
356-
357-
def je_ziva(self):
358-
return self.pocet_zivotu > 0
359-
360-
def uber_zivot(self):
361-
if not self.je_ziva():
362-
print("Nemuzes zabit uz mrtvou kocku!")
363-
else:
364-
self.pocet_zivotu -= 1
365-
366-
def snez(self, jidlo):
367-
if not self.je_ziva():
368-
print("Je zbytecne krmit mrtvou kocku!")
369-
return
370-
if jidlo == "ryba" and self.pocet_zivotu < 9:
371-
self.pocet_zivotu += 1
372-
print("Kocka spapala rybu a obnovil se ji jeden zivot.")
373-
else:
374-
print("Kocka se krmi.")
375-
```
376-
{% endfilter %}
377355

378356
A to je o samotných třídách zatím vše.
379357
[Příště](../inheritance/) si něco řekneme o dědičnosti.

0 commit comments

Comments
 (0)