Skip to content

Commit 6b03841

Browse files
authored
Modernize lessons on exceptions, modules and tests
#13
2 parents 5525da8 + 849644a commit 6b03841

File tree

12 files changed

+524
-205
lines changed

12 files changed

+524
-205
lines changed

courses/pyladies.yml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,23 @@ sessions:
7474
- lesson: beginners/nested-traceback
7575
- lesson: beginners/local-variables
7676

77-
- title: Testování
78-
slug: tests
77+
- title: Chyby a moduly
78+
slug: exc
7979
materials:
8080
- lesson: beginners/exceptions
8181
- lesson: beginners/modules
82-
- lesson: beginners/testing
83-
- lesson: beginners/circular-imports
82+
# XXX when homework is added, include lesson: beginners/circular-imports
8483
- title: Výjimkový tahák
8584
url: https://pyvec.github.io/cheatsheets/exceptions/exceptions-cs.pdf
8685
type: cheatsheet
8786

87+
- title: Rozhraní a testy
88+
slug: tests
89+
materials:
90+
- lesson: beginners/interfaces
91+
- lesson: beginners/testing
92+
- lesson: beginners/main-module
93+
8894
- title: Spolupráce a Open-Source
8995
slug: foss
9096
materials:

lessons/beginners/circular-imports/index.md

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
## Cyklické importy
22

3-
V domácích projektech budeš rozdělovat 1D Piškvorky na několik modulů.
4-
Výsledek bude vypadat třeba nějak takhle:
3+
V domácích úkolech budeš rozdělovat piškvorkový projekt na několik modulů.
4+
Tento text si doporučuju číst až když narazíš na příslušný úkol,
5+
abys věděl{{a}} o čem tu je řeč.
6+
7+
Po rozdělení bude projekt vypadat třeba nějak takhle:
58
(Šipky mezi moduly znázorňují importování.)
69

710
```plain
@@ -15,16 +18,6 @@ Výsledek bude vypadat třeba nějak takhle:
1518
└──────────────────┘ │ def tah_hrace │ └──────────────────┘
1619
│ │
1720
└───────────────┘
18-
19-
20-
│ ┌───────────────────╮
21-
│ │ test_piskvorky.py │
22-
│ ├───────────────────┤
23-
└─│ import piskvorky │
24-
├───────────────────┤
25-
│ def test_... │
26-
│ │
27-
└───────────────────┘
2821
```
2922

3023
Jenže funkce `tah_pocitace`
@@ -47,7 +40,7 @@ Můžeš importovat `ai` z `piskvorky` a zároveň
4740
└───────────────┘
4841
```
4942

50-
Můžeme se na to podívat z hlediska Pythonu,
43+
Můžeš se na to podívat z pohledu Pythonu,
5144
který příkazy v souborech vykonává.
5245
Když má importovat soubor `piskvorky.py`, začne ho
5346
zpracovávat řádek po řádku,
@@ -59,7 +52,7 @@ Brzy narazí na příkaz `import piskvorky`. Co teď?
5952
Aby nenastala situace podobná nekonečné smyčce –
6053
jeden soubor by importoval druhý, druhý zase první,
6154
a tak stále dokola –
62-
udělá Python taková malý „podvod“:
55+
udělá Python takový malý „podvod“:
6356
když zjistí, že soubor `piskvorky.py`
6457
už importuje, zpřístupní v modulu `ai`
6558
modul `piskvorky` tak, jak ho

lessons/beginners/exceptions/index.md

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,22 @@
33
Pojďme si prohloubit znalosti o chybách, neboli odborně o *výjimkách*
44
(angl. *exceptions*).
55

6-
Vezmi následující funkci:
6+
Podívej se na následující funkci:
77

88
```python
99
def nacti_cislo():
10+
"""Získá od uživatele celé číslo a vrátí ho"""
1011
odpoved = input('Zadej číslo: ')
1112
return int(odpoved)
1213
```
1314

1415
Když uživatel nezadá číslice, ale třeba text `cokolada`,
1516
nastane výjimka jménem `ValueError` (chyba hodnoty) a Python vypíše
16-
odpovídající chybovou hlášku.
17+
odpovídající chybovou hlášku:
1718

1819
```pycon
1920
Traceback (most recent call last):
20-
File "ukazka.py", line 3, in nacti_cislo
21+
File "ukazka.py", line 4, in nacti_cislo
2122
cislo = int(odpoved)
2223
ValueError: invalid literal for int() with base 10: 'cokolada'
2324
```
@@ -27,14 +28,15 @@ Co s tím má chudák funkce `int` dělat?
2728
Není žádná rozumná hodnota, kterou by mohla vrátit.
2829
Převádění tohoto textu na celé číslo nedává smysl.
2930

30-
Až funkce `nacti_cislo` nejlíp „ví“, co se má stát, když uživatel nezadá
31+
Až funkce `nacti_cislo` nejlíp „ví“, co se má stát když uživatel nezadá
3132
číslice.
3233
Stačí se uživatele zeptat znovu!
3334
Kdybys měl{{a}} funkci, která zjistí jestli jsou v řetězci jen číslice,
3435
mohlo by to fungovat nějak takhle:
3536

3637
```python
3738
def nacti_cislo():
39+
"""Získá od uživatele celé číslo a vrátí ho"""
3840
while True:
3941
odpoved = input('Zadej číslo: ')
4042
if obsahuje_jen_cislice(odpoved):
@@ -47,20 +49,20 @@ def nacti_cislo():
4749
Kde ale vzít funkci `obsahuje_jen_cislice`?
4850
Nemá smysl ji psát znovu – funkce `int` sama nejlíp pozná, co se dá převést na
4951
číslo a co ne.
50-
A dokonce nám to dá vědět – chybou, kterou můžeš *zachytit*.
52+
A dokonce nám to dá vědět – výjimkou, kterou můžeš *zachytit*.
5153

5254
> [note]
5355
> Ono „obsahuje_jen_cislice“ v Pythonu existuje. Dokonce několikrát.
5456
> Místo řešení problému to ale spíš ilustruje, v čem problém spočívá:
5557
> * Řetězcová metoda `isnumeric` vrací `True` pokud řetězec obsahuje číslice:
5658
> `'123'.isnumeric()` je pravda; `'abc'.isnumeric()` nepravda.
57-
> Problém je, že funkci `int` potřebuje jeden konkrétní druh číslic:
58-
> pro řetězce jako `'½'` nebo `'௩三๓໓`' (trojka v tamilském, japonském,
59-
> thajském nebo laoském písmu) platí `isnumeric`, ale `int` si na nich
59+
> Problém je, že funkce `int` potřebuje jeden konkrétní druh číslic:
60+
> pro řetězce jako `'½'` nebo `'௩三๓໓`' (trojky v tamilském, japonském,
61+
> thajském a laoském písmu) platí `isnumeric`, ale `int` si na nich
6062
> vyláme zuby stejně jako na `'abc'`.
6163
> * Řetězcová metoda `isdecimal` vrací `True` pokud řetězec obsahuje arabské
6264
> číslice 0-9. To už je lepší, ale stejně to úplně nesedí: `int` si poradí
63-
> s mezerou na začátku, např. s `' 3'`, ale funkce `isdecimal` takový řetězec
65+
> s mezerou na začátku, např. s `' 3'`. Funkce `isdecimal` ale takový řetězec
6466
> odmítne.
6567
>
6668
> Chceš-li zjistit jestli funkce `int` umí daný řetězec převést na číslo,
@@ -73,17 +75,19 @@ Pro zachycení chyby má Python příkaz `try`/`except`.
7375

7476
```python
7577
def nacti_cislo():
78+
"""Získá od uživatele celé číslo a vrátí ho"""
7679
while True:
7780
odpoved = input('Zadej číslo: ')
7881
try:
7982
return int(odpoved)
8083
except ValueError:
8184
print('To nebylo číslo!')
85+
# ... a zeptáme se znovu -- cyklus `while` pokračuje
8286
```
8387

8488
Jak to funguje?
8589
Příkazy v bloku uvozeném příkazem `try` se normálně provádějí, ale když
86-
nastane uvedená výjimka, Python přeskočí zbytek bloku `try` a provede všechno
90+
nastane uvedená výjimka, Python přeskočí zbytek bloku `try` a provede všechno
8791
v bloku `except`.
8892
Pokud výjimka nenastala, přeskočí se celý blok `except`.
8993

@@ -141,10 +145,10 @@ V našem příkladu to platí pro `ValueError` z funkce `int`: víš že uživ
141145
nemusí vždy zadat číslo ve správném formátu a víš že správná
142146
reakce na tuhle situaci je problém vysvětlit a zeptat se znovu.
143147

144-
Co ale dělat, kdyš uživatel chce ukončit program a zmáčkne
148+
Co ale dělat, když uživatel chce ukončit program a zmáčkne
145149
<kbd>Ctrl</kbd>+<kbd>C</kbd>?
146150
Nebo když se mu porouchá klávesnice a selže funkce `input`?
147-
Nejlepší reakce na takovou nečekanou situaci ukončit program a informovat
151+
Nejlepší reakce na takovou nečekanou situaci je ukončit program a informovat
148152
uživatele (nebo lépe, programátora), že (a kde) je něco špatně.
149153
Neboli vypsat chybovou hlášku.
150154
A to se stane normálně, bez `try`.
@@ -204,22 +208,23 @@ funkce mohla vrátit.
204208
Místo vrácení výsledku musí tato funkce *signalizovat chybu*.
205209
S tou se pak může program, který `obsah_ctverce(-5)` zavolal,
206210
vypořádat – vynadat uživateli, zkalibrovat měřák, nebo, pokud na chybu není
207-
připravený, sám skončit s chybou (a upozornit tak programátora, že je něco
208-
špatně).
211+
připravený, sám skončit s chybou a upozornit tak programátora, že je něco
212+
špatně.
209213

210214
Jak na to prakticky?
211215
Chybu můžeš vyvolat pomocí příkazu `raise`.
212216
Za příkaz dáš druh výjimky a pak do závorek nějaký popis toho, co je špatně.
213217

214218
```python
215219
def obsah_ctverce(strana):
220+
"""Vrátí obsah čtverce s danou délkou strany"""
216221
if strana > 0:
217222
return strana ** 2
218223
else:
219224
raise ValueError(f'Strana musí být kladná, číslo {strana} kladné není!')
220225
```
221226

222-
Podobně jako `return`, i příkaz `raise` ukončí funkci.
227+
Podobně jako `return` i příkaz `raise` ukončí funkci.
223228
A nejen tu – pokud na tuhle konkrétní chybu není program předem připravený,
224229
ukončí se celý program.
225230

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Rozhraní
2+
3+
Už víš že funkce ti umožňují kousek kódu:
4+
5+
* použít (zavolat) na více místech v programu, i když definice je jen jedna,
6+
* vyčlenit, aby detail (jako načtení čísla od uživatele) „nezavazel“ ve větším
7+
programu, který tak může být přehlednější, a
8+
* pojmenovat, aby bylo jasné co kód dělá i bez toho, abys musel{{a}} číst
9+
samotné tělo funkce.
10+
11+
Další výhoda funkce je, že ji můžeš jednoduše vyměnit za jinou,
12+
lepší funkci – pokud má ta lepší funkce stejné *rozhraní* (angl. *interface*).
13+
14+
Aby se ti líp představovalo, o čem budeme povídat, představ si elektrickou
15+
zásuvku ve zdi.
16+
Do takové zásuvky můžeš zapojit počítač, lampu, nabíječku na mobil, vysavač,
17+
nebo rádio.
18+
Zásuvka poskytuje elektrický proud; je jedno, jak ho použiješ.
19+
Stejně tak je jedno jestli je „druhý konec“ zásuvky připojený k solárnímu
20+
panelu nebo k atomové elektrárně.
21+
Zásuvka poskytuje elektrický proud, a jsou u ní důležité určité parametry
22+
(tvar, napětí, frekvence, maximální proud) na kterých se obě strany,
23+
poskytovatel proudu i spotřebič, shodly.
24+
Tyhle parametry tvoří *rozhraní*, které umožňuje připojit jakýkoli spotřebič
25+
k jakékoli elektrárně.
26+
27+
28+
## Rozhraní funkce
29+
30+
Podívej se na tuhle hlavičku funkce.
31+
Je z ní poznat, co ta funkce dělá a jak ji použít?
32+
33+
```python
34+
def ano_nebo_ne(otazka):
35+
"""Zeptá se uživatele na otázku a vrátí True nebo False dle odpovědi"""
36+
...
37+
```
38+
39+
Podobnou funkci už jsi napsal{{a}}.
40+
Když zavoláš `ano_nebo_ne('Chutná ti čokoláda?')`, otázka se objeví
41+
na příkazové řádce.
42+
Když uživatel odpoví, funkce vrátí True nebo False.
43+
44+
Co kdybys ale měl{{a}} následující funkci?
45+
46+
```python
47+
def ano_nebo_ne(otazka):
48+
"""Ukáže tlačítka "Ano" a "Ne" a až uživatel jedno zmáčkne, vrátí True
49+
nebo False dle stisknutého tlačítka."""
50+
...
51+
```
52+
53+
<img src="{{ static('yn.png') }}" alt="Screenshot s tlačítky Ano a Ne" style="display:block;float:right;">
54+
55+
Když zavoláš tuhle funkci, `ano_nebo_ne('Chutná ti čokoláda?')`, ukáže se
56+
okýnko se dvěma tlačítky.
57+
Když uživatel jedno zmáčkne, funkce vrátí True nebo False.
58+
59+
Z hlediska programu se nic nemění: jediné co se změní je *definice funkce*;
60+
volání a práce s návratovou hodnotou je pak stejné jako dřív.
61+
62+
63+
### Vyzkoušej si to!
64+
65+
Najdi nějaký svůj program, který používá `ano_nebo_ne`, případně jen `print`
66+
a `input`.
67+
68+
Stáhni si modul <a href="{{ static('tkui.py') }}"><code>tkui.py</code></a>
69+
do adresáře se svým programem.
70+
Naimportuj z něho funkce, které potřebuješ.
71+
Jsou k dispozici čtyři:
72+
73+
```python
74+
from tkui import input, nacti_cislo, ano_nebo_ne, print
75+
```
76+
77+
Tento import *přepíše* vestavěné funkce `input` a `print` variantami,
78+
které mají (téměř) stejné rozhraní – jen dělají něco trochu jinak.
79+
80+
Případné vlastní definice funkcí `nacti_cislo` a `ano_nebo_ne` pak z programu
81+
vyndej, aby se použily ty naimportované.
82+
83+
> [note]
84+
> Funkce `tkui.nacti_cislo` potřebuje Python verze 3.7 nebo vyšší.
85+
> Používáš-li Python 3.6, `nacti_cislo` nenahrazuj.
86+
87+
Program by měl fungovat stejně jako dřív!
88+
89+
Je to tím, že tyto funkce mají stejné *rozhraní* jako jejich dřívější protějšky.
90+
Rozhraní funkce tvoří všechno, co potřebuje kód který funkce volá:
91+
92+
* jméno, kterým se funkce volá,
93+
* argumenty, které bere (např. `input` bere otázku jako řetězec; `print`
94+
může brát více argumentů k vypsání), a
95+
* návratová hodnota, se kterou program pracuje dál (např. `input` vrací
96+
řetězec; `print` nevrací nic smysluplného).
97+
98+
Nékteré z těchto informací musíš do hlavičky funkce napsat vždy.
99+
Ty ostatní je dobré popsat v dokumentačním řetězci, aby ten, kdo chce funkci
100+
použít, věděl jak na to.
101+
102+
103+
> [note]
104+
> Modul `tkui` je jen ilustrační. Je udělaný tak, aby se dobře “instaloval”
105+
> spíš než aby ti pomohl psát reálné programy.
106+
> V tomto kurzu se vrátíme zpět k příkazové řádce, která je dělaná tak
107+
> aby byla užitečná pro programátory.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
title: Rozhraní
2+
style: md
3+
attribution: Pro PyLadies Brno napsal Petr Viktorin, 2020.
4+
license: cc-by-sa-40

0 commit comments

Comments
 (0)