diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 97fe9888..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Compile course -on: - push: - pull_request: - workflow_dispatch: -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.13 - - name: Poetry cache - uses: actions/cache@v4 - with: - path: ~/.cache/pypoetry - key: poetry-cache-${{ runner.os }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('.github/workflows/*.yml') }} - - name: Pip cache - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: pip-cache-${{ runner.os }}-${{ hashFiles('.github/workflows/*.yml') }} - - name: Install dependencies - run: | - python -m pip install poetry - poetry install -E ghp-compiled --no-root - - name: Compile the courses - run: | - poetry run python -m naucse_render compile _compiled \ - --edit-repo-url https://github.com/${{ github.repository }} \ - --edit-repo-branch ${{ github.ref_name }} - - if: ${{ startsWith(github.ref, 'refs/heads/') }} - name: Publish compiled courses - run: | - git fetch origin compiled/${{ github.ref_name }} || : - poetry run python -m ghp_import -m "Compiled" -b compiled/${{ github.ref_name }} --push _compiled/ - curl -H 'Content-Type: application/json' \ - --data '{"repository": "https://github.com/${{ github.repository }}", "branch": "compiled/${{ github.ref_name }}"}' \ - https://hooks.nauc.se/trigger - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GIT_AUTHOR_NAME: ghp_import - GIT_COMMITTER_NAME: ghp_import - EMAIL: none@invalid diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8a029b20..00000000 --- a/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -*.pyc - -# LaTeX -*.aux -*.log -*.out - -# Notebook -.ipynb_checkpoints/ - -venv -_build -.arca -.pytest_cache diff --git a/README.md b/README.md deleted file mode 100644 index a42498e9..00000000 --- a/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Nauč se Python - -Otevřené materiály pro výuku Pythonu. - -Tento repozitář obsahuje kurzy pro samouky, které najdeš (se spoustou dalších) -na [naucse.python.cz](https://naucse.python.cz). - - -## Instalace a spuštění - -Chceš-li server spustit na svém počítači, např. proto, že se chceš zapojit -do vývoje, je potřeba ho nejdřív nainstalovat: - -* Přepni se do adresáře s kódem projektu. -* Nainstaluj `poetry`. Můžeš instalovat podle [oficiálního návodu](https://python-poetry.org/docs/#installation), nebo: - - * Vytvoř a aktivuj si [virtuální prostředí](https://naucse.python.cz/lessons/beginners/install/) v Pythonu 3.9+. - - * Linux/MacOS: - - ```console - $ python3 -m pip install poetry - ``` - - * Windows: - - ```doscon - > py -3 -m pip install poetry - ``` - -* Vytvoř si prostředí a nainstaluj závislosti: - - ```doscon - $ poetry install - ``` - -Nainstalovanou aplikaci spustíš následovně: - -* Spusť vývojový server: - ```console - $ poetry run python -m naucse serve - ``` -* Program vypíše adresu (např. `http://127.0.0.1:8003/`); tu navštiv v prohlížeči. - - -## Licence - -Obsah kurzů má vlastní licenci, která je uvedena v metadatech. -Používáme pouze [licence pro otevřený obsah][free content licenses]. -Všechen obsah musí mít uvedenou licenci. - ---- - -Content has its own license specified in the appropriate matadata. -Only [free content licenses] are used. By contributing to an already licensed -document, you agree to have it licensed under the same license. -(And feel free to add yourself to the authors list in its metadata.) -When contributing new document(s) a license must be specified in the metadata. - -[free content licenses]: https://en.wikipedia.org/wiki/List_of_free_content_licenses diff --git a/courses/gitworking.yml b/courses/gitworking.yml deleted file mode 100644 index 8ffe2949..00000000 --- a/courses/gitworking.yml +++ /dev/null @@ -1,25 +0,0 @@ -title: Gitworking -subtitle: Celodenní git workshop pro úplné začátečníky -description: Git pro začátečnice a začátečníky, základní znalost Pythonu výhodou -long_description: | - Umíš základy Pythonu (například ze začátečnického kurzu PyLadies), ale chceš se přiučit něco o gitu? - - Tento workshop je pro všechny, kteří jsou pokročilejší začátečníci – umí pracovat s příkazovou řádkou zhruba na úrovni absolventek začátečnického kurzu PyLadies, vědí, jak fungují soubory na počítači a že se do nich píše zdrojový kód. - - Budeme používat Python, ale o programování tolik nepůjde. -plan: - - title: Git - slug: git - materials: - - lesson: git/install - - lesson: git/basics - - lesson: git/branching - - lesson: git/collaboration - - lesson: git/ignoring - - lesson: beginners/cmdline - type: link - - title: Tahák na git - url: https://pyvec.github.io/cheatsheets/basic-git/basic-git-cs.pdf -vars: - coach-present: true - use-gender: f diff --git a/courses/meta.yml b/courses/meta.yml deleted file mode 100644 index a22cb37b..00000000 --- a/courses/meta.yml +++ /dev/null @@ -1,26 +0,0 @@ -title: Jak přidat kurz na Nauč se Python -description: Návod na přidání kurzu na Nauč se Python -long_description: | - - V tomto meta-kurzu je kompletní návod na přidávání kurzů na Nauč se Python. - - Kurz je určený pro organizátory a k přidání kurzu jsou potřeba jen základní znalosti práce - s gitem a GitHubem. -canonical: true -meta: true -sessions: -- title: Přidání kurzu - slug: adding-a-run - materials: - - lesson: meta/local-run - - lesson: meta/submitting-a-run - -extra_lessons: -- git/install -- git/branching -- git/git-collaboration-2in1 -- beginners/install -- beginners/cmdline -- beginners/venv-setup -- intro/notebook -- fast-track/yaml diff --git a/courses/mi-pyt.yml b/courses/mi-pyt.yml deleted file mode 100644 index 1184c144..00000000 --- a/courses/mi-pyt.yml +++ /dev/null @@ -1,89 +0,0 @@ -title: MI-PYT -description: Základy již znáte a chcete se dozvědět o dalších možnostech využití Pythonu. -long_description: | - Díváte se na materiály předmětu MI-PYT (Pokročilý Python) na FIT ČVUT. - Materiály jsou dostupné v dvojí podobě, interně pro studenty FITu - na *Course Pages* a pro všechny veřejně zde. - - Tento kurz vzniká pod záštitou firmy Red Hat Czech, s.r.o. - -canonical: true -vars: - coach-present: false - mi-pyt: true -sessions: -- title: Weboví klienti & CLI – Requests a Click - slug: requests-click - materials: - - lesson: fast-track/install - - lesson: intro/requests - - lesson: intro/click - -- title: Webové aplikace – Flask - slug: flask - materials: - - lesson: intro/flask - - lesson: intro/deployment - -- title: Moduly - slug: distribution - materials: - - lesson: intro/distribution - -- title: Testování - slug: testing - materials: - - lesson: intro/testing - -- title: Dokumentace – Sphinx - slug: docs - materials: - - lesson: intro/docs - -- title: NumPy - slug: numpy - materials: - - lesson: intro/notebook - - lesson: intro/numpy - - title: Tahák na NumPy - url: https://pyvec.github.io/cheatsheets/numpy/numpy-cs.pdf - type: cheatsheet - -- title: Analýza dat – Pandas - slug: pandas - materials: - - lesson: intro/pandas - -- title: Cython - slug: cython - materials: - - lesson: intro/cython - -- title: GUI – PyQT - slug: qt - materials: - - lesson: intro/pyqt - -- title: Generátory a AsyncIO - slug: async - materials: - - lesson: advanced/generators - - lesson: intro/async - -- title: Magie - slug: magic - materials: - - lesson: intro/magic - -- title: MicroPython - slug: upy - materials: - - lesson: intro/micropython - -extra_lessons: -- beginners/install -- beginners/cmdline -- beginners/testing -- fast-track/http -- git/git-collaboration-2in1 -- beginners/venv-setup diff --git a/courses/pyladies.yml b/courses/pyladies.yml deleted file mode 100644 index 2b014a91..00000000 --- a/courses/pyladies.yml +++ /dev/null @@ -1,157 +0,0 @@ -title: Začátečnický kurz -description: Naučte se Python vážně od začátku. Žádné předchozí znalosti nejsou potřeba. -long_description: | - - Zde najdeš materiály, které se používají na začátečnických kurzech PyLadies - v Praze, Brně a Ostravě. - - Jednotlivé lekce jsou určeny naprostým začátečníkům, žádné předchozí - znalosti nejsou nutné. Instrukce jsou uvedeny pro operační systémy Linux, - Windows i macOS. -canonical: true -vars: - coach-present: false -sessions: -- title: Instalace - slug: install - materials: - - lesson: beginners/cmdline - - lesson: beginners/install - - lesson: beginners/venv-setup - - lesson: beginners/first-steps - - lesson: beginners/install-editor - - lesson: git/install - - title: Tahák na klávesnici (PDF) - url: https://pyvec.github.io/cheatsheets/keyboard/keyboard-cs.pdf - type: cheatsheet - -- title: První program - slug: hello - materials: - - lesson: beginners/hello-world - - lesson: beginners/print - - lesson: beginners/variables - - lesson: beginners/comparisons - - lesson: beginners/and-or - -- title: Cykly - slug: loops - materials: - - lesson: beginners/expressions - - lesson: beginners/functions - - lesson: beginners/basic-functions - - lesson: intro/turtle - - lesson: beginners/while - - lesson: beginners/reassignment - - title: Tahák s užitečnými funkcemi - url: https://pyvec.github.io/cheatsheets/basic-functions/basic-functions-cs.pdf - type: cheatsheet - -- title: Správa zdrojového kódu - slug: git - materials: - - lesson: git/basics - - lesson: git/branching - - title: Gitový tahák - url: https://pyvec.github.io/cheatsheets/basic-git/basic-git-cs.pdf - type: cheatsheet - -- title: Řetězce - slug: def-str - materials: - - lesson: beginners/str - - lesson: beginners/str-index-slice - - lesson: beginners/str-methods - - lesson: beginners/fstring - - title: Řetězcový tahák - url: https://pyvec.github.io/cheatsheets/strings/strings-cs.pdf - type: cheatsheet - -- title: Definice funkcí - slug: def - materials: - - lesson: beginners/def - - lesson: beginners/prefer-return - - lesson: beginners/nested-traceback - - lesson: beginners/local-variables - -- title: Chyby a moduly - slug: exc - materials: - - lesson: beginners/exceptions - - lesson: beginners/modules - # XXX when homework is added, include lesson: beginners/circular-imports - - title: Výjimkový tahák - url: https://pyvec.github.io/cheatsheets/exceptions/exceptions-cs.pdf - type: cheatsheet - -- title: Rozhraní a testy - slug: tests - materials: - - lesson: beginners/interfaces - - lesson: beginners/testing - - lesson: beginners/main-module - -- title: Spolupráce a Open-Source - slug: foss - materials: - - lesson: git/collaboration - - lesson: git/ignoring - - title: Gitový tahák - url: https://pyvec.github.io/cheatsheets/basic-git/basic-git-cs.pdf - type: cheatsheet - -- title: Seznamy - slug: list - materials: - - lesson: beginners/list - - lesson: beginners/tuple - - lesson: beginners/nested-list - - title: Tahák na seznamy - url: https://pyvec.github.io/cheatsheets/lists/lists-cs.pdf - type: cheatsheet - -- title: Sekvence a soubory - slug: seq - materials: - - lesson: beginners/range - - lesson: beginners/zip-enumerate - - lesson: beginners/files - - lesson: beginners/with - -- title: Grafika - slug: pyglet - materials: - - lesson: intro/pyglet - - lesson: projects/pong - - title: Tahák na Pyglet - url: https://pyvec.github.io/cheatsheets/pyglet/pyglet-basics-cs.pdf - type: cheatsheet - -- title: Slovníky - slug: dict - materials: - - lesson: beginners/dict - - lesson: intro/json - - lesson: projects/github-api - - lesson: beginners/dict-with-list-values - - title: Slovníkový tahák - url: https://pyvec.github.io/cheatsheets/dicts/dicts-cs.pdf - type: cheatsheet - -- title: Třídy - slug: class - materials: - - lesson: beginners/class - - lesson: beginners/inheritance - -- title: Závěrečný projekt - slug: asteroids - materials: - - lesson: projects/asteroids - - title: Množinový tahák - url: https://pyvec.github.io/cheatsheets/sets/sets-cs.pdf - type: cheatsheet - - title: Tahák na geometrii a fyziku 2D her - url: https://pyvec.github.io/cheatsheets/game-physics/game-physics-cs.pdf - type: cheatsheet diff --git a/lessons/advanced/generators/index.md b/lessons/advanced/generators/index.md deleted file mode 100644 index 53e3c1cc..00000000 --- a/lessons/advanced/generators/index.md +++ /dev/null @@ -1,337 +0,0 @@ -Generátory -========== - - -Dnes si popíšeme, jak v Pythonu fungují *generátory*, tedy funkce s příkazem `yield`. -Někteří z vás možná už nějaký jednoduchý generátor napsali, ale pojďme si je -vysvětlit od úplného začátku: od toho, jak se v Pythonu iteruje. - - -Iterace -------- - -Když je v Pythonu potřeba iterovat (např. příkazem `for`) přes nějakou kolekci -(seznam, řetězec, soubor, …), použije se *iterační protokol*, -který pracuje se dvěma druhy objektů: s *iterovatelnými objekty* a s *iterátory*. - -Iterovatelné objekty (*iterables*) se vyznačují tím, že je na ně možné zavolat -funkci `iter()`. Ta vrátí příslušný iterátor: - -```pycon ->>> iter([1, 2, 3]) - -``` - -Na iterátor pak je možné opakovaně volat funkci `next()`, čímž dostáváme jednotlivé -prvky iterace. -Po vyčerpání iterátoru způsobuje `next()` výjimku `StopIteration`: - -```pycon ->>> it = iter([1, 2, 3]) ->>> next(it) -1 ->>> next(it) -2 ->>> next(it) -3 ->>> next(it) -Traceback (most recent call last): - ... -StopIteration ->>> next(it) -Traceback (most recent call last): - ... -StopIteration -``` - -Cyklus `for x in collection: ...` tedy dělá něco jako: - -```python -iterator = iter(collection) -while True: - try: - x = next(iterator) - except StopIteration: - break - - ... # tělo původního cyklu -``` - -Platí, že každý iterátor je iterovatelný: zavoláním `iter()` na iterátor -dostaneme ten stejný iterátor (nikoli jeho kopii) zpět. -Naopak to ale obecně neplatí: např. seznamy jsou iterovatelné, ale nejsou samy -o sobě iterátory. -Každé zavolání `iter(seznam)` vrací nový iterátor, který má svou vlastní -„aktuální pozici“ a iteruje od začátku. - -Iterátor je ve většině případů „malý“ objekt, který si „pamatuje“ jen původní iterovatelný -objekt a aktuální pozici. Příklady jsou iterátory seznamů (`iter([])`), slovníků (`iter({})`), -*n*-tic nebo množin, iterátor pro `range` a podobně. - -Iterátory ale můžou být i „větší“: třeba otevřený soubor je iterátor, z něhož `next()` -načítá jednotlivé řádky. - - -Generátory ----------- - -Asi nejzajímavější druh iterátoru je tzv. *generátor*: funkce, která umí postupně -dávat k dispozici hodnoty. -Definuje se pomocí klíčového slova `yield`: každá funkce, která obsahuje `yield`, -je *generátorová funkce* (angl. *generator function*). - -```python -def generate2(): - """generates 2 numbers""" - print('A') - yield 0 - print('B') - yield 1 - print('C') -``` - -Zavoláním takové funkce dostáváme *generátorový iterátor* (angl. *generator iterator*): - -```pycon ->>> generate2() - -``` - -Voláním `next()` se pak stane zajímavá věc: funkce se provede až po první `yield`, -tam se *zastaví* a hodnota `yield`-u se vrátí z `next()`. -Při dalším volání se začne provádět zbytek funkce od místa, kde byla naposled -zastavena. - -```pycon ->>> it = generate2() ->>> next(it) -A -0 ->>> next(it) -B -1 ->>> next(it) -C -Traceback (most recent call last): - ... -StopIteration -``` - - -Další použití generátorů ------------------------- - -Vlastnost přerušit provádění funkce je velice užitečná nejen pro vytváření -sekvencí, ale má celou řadu dalších užití. -Existuje třeba dekorátor, který generátorovou funkci s jedním `yield` převede na *context manager*, -tedy objekt použitelný s příkazem `with`: - -```python -import contextlib - -@contextlib.contextmanager -def ctx_manager(): - print('Entering') - yield 123 - print('Exiting') - - -with ctx_manager() as obj: - print('Inside context, with', obj) -``` - -Vše před `yield` se provede při vstupu do kontextu, hodnota `yield` se předá -dál a vše po `yield` se provede na konci. -Můžeme si představit, že místo `yield` se „doplní“ obsah bloku `with`. -Funkce se tam na chvíli zastaví a může se tedy provádět něco jiného. - - -Vracení hodnot z generátorů ---------------------------- - -V rámci generátorové funkce můžeme použít i `return`, který funkci ukončí. -Vrácená hodnota se však při normální iteraci (např. ve `for`) nepoužije. -Objeví se pouze jako hodnota výjimky `StopIteration`, která signalizuje konec -iterace: - -```python -def generator(a, b): - """Yield two numbers and return their sum""" - yield a - yield b - return a + b -``` - -```pycon ->>> it = generator(2, 3) ->>> next(it) -2 ->>> next(it) -3 ->>> next(it) -Traceback (most recent call last): - File "", line 1, in -StopIteration: 5 -``` - - -Obousměrná komunikace ---------------------- - -Oproti normálním iterátorům, které hodnoty jen poskytují, mají generátory metodu -`send()`, kterou je možné posílat hodnoty *do* běžícího generátoru. -Klíčové slovo `yield` totiž může fungovat jako výraz a tento výraz nabývá poslanou -hodnotu (nebo `None`, byl-li použit normální `next()`). - -```python -def running_sum(): - total = 0 - while True: - num = (yield total) - if num: - total += num - -it = running_sum() -next(it) # pro první iteraci nelze použít send() -- nečekáme zatím na yield-u -it.send(2) -it.send(3) -assert next(it) == 5 -``` - -Upřímě řečeno, metoda `send()` není příliš užitečná. -(Když byste něco takového potřebovali, radši si napište třídu, která si bude -stav uchovávat v atributech, a měňte ji třeba metodami. Bude to pravděpodobně -přehlednější.) -Existuje ale příbuzná metoda, která už je užitečnější: `throw()`. -Ta do generátoru „vhodí“ výjimku. -Z pohledu generátorové funkce to vypadá, jako by výjimka nastala na příkazu -`yield`. - -```python -def report_exception(): - try: - yield - except BaseException as e: - print('Death by', type(e).__name__) - yield 123 -``` - -```pycon ->>> it = report_exception() ->>> next(it) # opět – v první iteraci nelze throw() použít ->>> value = it.throw(ValueError()) -Death by ValueError ->>> value -123 -``` - -Podobná věc se děje, když generátorový iterátor zanikne: Python do generátoru -„vhodí“ výjimku GeneratorExit. -Ta dědí z `BaseException`, ale ne `Exception`, takže klasické `except Exception:` -ji nechytí (ale např. `finally` funguje jak má). -Pokud generátor tuto výjimku chytá, měl by se co nejdřív ukončit. -(Když to neudělá a provede další `yield`, Python ho ukončí „násilně“.) - -```pycon ->>> import gc ->>> it = report_exception() ->>> next(it) ->>> del it; gc.collect() # zbavíme se objektu "it" -Death by GeneratorExit -Exception ignored in: -RuntimeError: generator ignored GeneratorExit -0 -``` - - -Delegace na podgenerátor – `yield from` ---------------------------------------- - -Máme následující generátor: - -```python -def dance(): - yield 'putting hands forward' - yield 'putting hands down' - yield 'turning around' - yield 'jumping' - yield 'putting hands forward' - yield 'putting hands down' - -for action in dance(): - print(action) -``` - -Opakuje se v něm jistá sekvence, kterou bychom jako správní programátoři chtěli -vyčlenit do samostatné funkce. -Pomocí samotného `yield` to ale jde celkem těžko: - -```python -def dance_hands(): - yield 'putting hands forward' - yield 'putting hands down' - -def dance(): - for action in dance_hands(): - yield action - yield 'turning around' - yield 'jumping' - for action in dance_hands(): - yield action - -for action in dance(): - print(action) -``` - -Tohle počtu řádků příliš nepomohlo. Existuje lepší způsob – místo cyklu -můžeme použít `yield from`: - - -```python -def dance_hands(): - yield 'putting hands forward' - yield 'putting hands down' - -def dance(): - yield from dance_hands() - yield 'turning around' - yield 'jumping' - yield from dance_hands() - -for action in dance(): - print(action) -``` - -Navíc lze `yield from` použít jako výraz, který nabývá hodnoty -kterou podgenerátor vrátil (t.j. hodnota výjimky `StopIteration`). - -```python -def fibonacci(limit): - a = 0 - b = 1 - count = 0 - while b < limit: - yield b - count += 1 - a, b = b, a + b - return count - -def fib_annotated(): - count = (yield from fibonacci(10)) - print('LOG: Generated {} numbers'.format(count)) - -for value in fib_annotated(): - print('Got', value) -``` - -``` -Got 1 -Got 1 -Got 2 -Got 3 -Got 5 -Got 8 -LOG: Generated 6 numbers -``` - -Kromě toho `yield from` deleguje hodnoty poslané pomocí `send()` či `throw()`. diff --git a/lessons/advanced/generators/info.yml b/lessons/advanced/generators/info.yml deleted file mode 100644 index f2335bd9..00000000 --- a/lessons/advanced/generators/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Generátory -style: md -attribution: Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017. -license: cc-by-sa-40 diff --git a/lessons/and-or/index.html b/lessons/and-or/index.html new file mode 100644 index 00000000..da4a94e4 --- /dev/null +++ b/lessons/and-or/index.html @@ -0,0 +1,93 @@ +

Nebo anebo a +# +

+

Vzpomínáš na tabulku operátorů +z lekce o Porovnávání? +Nyní si ji doplníme o další tři operátory, +které se hodí do podmínek:

+ + + + + + + + + + + + + + + + + + + + +
SymbolPříkladPopis
andTrue and False
2 < 3 and 5 < 3
„a zároveň“
orTrue or False
2 < 3 or 5 < 3
„a nebo“
notnot False
not 5 < 3 +
„ne“
+ +

Například, chceš-li zjistit, jestli je kterékoli z dvou čísel záporné, +můžeš napsat:

+
a = float(input("Zadej první stranu obdélníka: "))
+b = float(input("Zadej druhou stranu obdélníka: "))
+
+if a <= 0 or b <= 0:
+    print("Délka nemůže být záporná!")
+

Falešní kamarádi

+

Pozor na to, že and a or nejsou anglická slovíčka, ale operátory, +které spojují logické výrazy. +Na obě strany and i or patří výraz, jehož hodnota je True/False +(například porovnání).

+
if a <= 0 or b <= 0:
+

Může se zdát, že by se to dalo zkrátit a napsat if a or b <= 0: – „pokud +je A nebo B menší než 0“. +Ale to si Python přeloží na:

+
if (a) or (b <= 0):
+

... tedy „pokud platí A, a nebo je B menší než 0“. +A to moc smyslu nedává. +(Kdy „platí“ celé číslo?)

+

Šťastná/Bohatá +# +

+

Pro příklad použijeme and ve vylepšeném programu, který rozdává nejapné rady +do života. +Zkus si ho projít a okomentovat části, které nejsou na první pohled jasné.

+
# Tento program rozdává nejapné rady do života.
+
+print('Odpovídej "ano" nebo "ne".')
+stastna_retezec = input('Jsi šťastná? ')
+if stastna_retezec == 'ano' or stastna_retezec == 'Ano':
+    stastna = True
+elif stastna_retezec == 'ne' or stastna_retezec == 'Ne':
+    stastna = False
+else:
+    print('Nerozumím!')
+
+bohata_retezec = input('Jsi bohatá? ')
+if bohata_retezec == 'ano' or bohata_retezec == 'Ano':
+    bohata = True
+elif bohata_retezec == 'ne' or bohata_retezec == 'Ne':
+    bohata = False
+else:
+    print('Nerozumím!')
+
+if bohata and stastna:
+    # Je bohatá a zároveň štǎstná, ta se má.
+    print('Gratuluji!')
+elif bohata:
+    # Je bohatá, ale není „bohatá a zároveň šťastná“,
+    # takže musí být jen bohatá.
+    print('Zkus se víc usmívat.')
+elif stastna:
+    # Tady musí být jen šťastná.
+    print('Zkus míň utrácet.')
+else:
+    # A tady víme, že není ani šťastná, ani bohatá.
+    print('To je mi líto.')
+

Všimni si, co se stane, když zadáš něco jiného než „ano“ nebo „ne“.

+

Proměnná stastna nebo bohata se nenastaví, a když je ji potom +potřeba použít, program skončí s chybou.

+

O tom, jak se vypořádat s chybami, si povíme později.

+
\ No newline at end of file diff --git a/lessons/asteroids/index.html b/lessons/asteroids/index.html new file mode 100644 index 00000000..02f48ff7 --- /dev/null +++ b/lessons/asteroids/index.html @@ -0,0 +1,354 @@ +

Hra typu Asteroids +# +

+

Dnes to všechno – třídy, grafiku, seznamy, a tak dále – +spojíme dohromady do závěrečného projektu. +Doufám, že se ti bude líbit!

+

Zkusíme udělat klon hry Asteroids, +která poprvé vyšla na konci sedmdesátých let. +V našem podání bude hra nakonec vypadat nějak takhle:

+

Screenshot hry typu Asteroids

+

Projekt je to docela složitý a – jako většina +praktických projektů – využívá i některých věcí, které ještě na kurzu nebyly. +Věřím, že ale přesto zvládneš všechno pochopit nebo dohledat!

+

A ještě jedna věc: protože začátečnický kurz končí, +začneme kód psát v angličtině, aby se pak dal sdílet s celým světem.

+

Procházíš-li si projekt doma, je možné, že narazíš na +něco s čím si nebudeš vědět rady. +Kdyby se to stalo, prosím, ozvi se nám! +Rádi ti s projektem pomůžeme.

+

Vesmírná loď +# +

+

První krok bude naprogramovat vesmírnou loď, která půjde ovládat klávesnicí.

+
    +
  • Vesmírnou loď bude reprezentovat objekt třídy Spaceship.

    +
  • +
  • Každá loď má vlastní atributy x a y (pozice), +x_speed a y_speed (rychlost), rotation (úhel natočení) a +sprite (2D objekt v Pygletu s polohou, rotací a obrázkem).

    +
  • +
  • Loď má metodu tick, která obstarává +mechaniku týkající se lodi – posouvání, natáčení a ovládání.

    +
  • +
  • Všechny objekty ve hře si budeme dávat do globálního seznamu objects. +Zatím tam bude jenom vesmírná loď.

    +
  • +
  • Co se ovládání týče, stisknuté klávesy si uchovávej v množině (angl. set), +což je datový typ podobný seznamu, jen nemá dané pořadí +prvků a každý prvek v ní může být pouze jednou. +(Na množinu se dá dívat i jako na slovník bez hodnot.) +Je k dispozici tahák na množiny +a pythonní dokumentace obsahuje k množinám +tutoriál +i podrobný popis. +Vesmírná loď se pak do množiny „podívá” v rámci +své metody tick.

    +
  • +
  • Můžeš použít sadu obrázků, +které nakreslil Kenney Vleugels +a zpřístupnil je zadarmo komukoli. Nebo si nakresli/stáhni vlastní!

    +
  • +
  • Ve hře později použijeme velké množství +Sprite-ů a vykreslovat je jeden po druhém by trvalo docela dlouho. +Všechny Sprite-y proto přidej do kolekce +pyglet.graphics.Batch, +kterou pak Pyglet umí efektivně vykreslit najednou. +Do „batche” jde přidávat pomocí argumentu při vytváření Sprite() +a odebírat pomocí sprite.delete(). Například:

    +
    batch = pyglet.graphics.Batch()
    +sprite1 = pyglet.sprite.Sprite(obrazek, batch=batch)
    +sprite2 = pyglet.sprite.Sprite(obrazek, batch=batch)
    +
    +# a potom můžeš vykreslit všechny najednou:
    +batch.draw()
    +

    Kolekci batch si stejně jako objects uchovávej globálně.

    +
  • +
  • Aby se objekty hýbaly a otáčely podle svých středů, je dobré nastavit „kotvu“ +obrázku na jeho střed (jinak je kotva v levém dolním rohu):

    +
    image = pyglet.image.load(...)
    +image.anchor_x = image.width // 2
    +image.anchor_y = image.height // 2
    +self.sprite = pyglet.sprite.Sprite(image, batch=batch)
    +
  • +
  • Pro pohyb raketky půjde použít klávesy s šipkami doleva, doprava a rovně. +Šipky do stran raketu točí, šipka dopředu zrychluje pohyb tím směrem, kam je +raketka otočená.

    +
      +
    • Základní pohyb raketky je jednoduchý: k x-ové +souřadnici se přičte x-ová rychlost krát uplynulý čas +a to samé v y-ové souřadnici i pro úhel otočení:

      +
      self.x = self.x + dt * self.x_speed
      +self.y = self.y + dt * self.y_speed
      +self.rotation = self.rotation + dt * rotation_speed
      +

      Rychlost otáčení závisí na stisknutých šipkách (doleva nebo doprava). +V jednom případě je záporná, v druhém kladná. Vhodnou hodnotu zvol +experimentováním – začni třeba u 4 radiánů za sekundu. +Všechny podobné „magické hodnoty“ je vhodné definovat +jako konstanty – tedy proměnné, které na začátku nastavíš a nikdy +je neměníš. Bývá zvykem je označovat velkými písmeny a dávat je na +začátek souboru, hned za importy:

      +
      ROTATION_SPEED = 4  # radians per second
      +
    • +
    • Zrychlení je trochu složitější: k x-ové rychlosti +se přičte kosinus úhlu otočení krát uplynulý čas. +U y-ové osy se použije sinus.

      +
      self.x_speed += dt * ACCELERATION * math.cos(self.rotation)
      +self.y_speed += dt * ACCELERATION * math.sin(self.rotation)
      +

      Všimni si v příkladu konstanty ACCELERATION. Tu opět zvol podle uvážení.

      +
    • +
    • Když máš hodnoty self.x, self.y a self.rotation spočítané, nezapomeň +je promítnout do self.sprite, jinak se nic zajímavého nestane.

      +

      Pozor na to, že funkce math.sin a math.cos používají radiány, +kdežto pyglet používá pro Sprite.rotation stupně. +(A k tomu je navíc 0° jinde, a otáčí se na opačnou stranu.) +Pro sprite je tedy potřeba úhel převést:

      +
      self.sprite.rotation = 90 - math.degrees(self.rotation)
      +self.sprite.x = self.x
      +self.sprite.y = self.y
      +
    • +
    • Když raketka vyletí z okýnka ven, vrať +ji zpátky do hry na druhé straně okýnka. +(Zkontroluj si, že to funguje na všech čtyřech stranách.)

      +
    • +
    +
  • +
  • Bonus 1: Zkus si přidat několik raketek, +každou trochu jinak natočenou.

    +

    Každý jednotlivý objekt třídy Spaceship +si udržuje vlastní stav, takže by nemělo být složité +jich vytvořit víc (a všechny ovládat najednou).

    +
  • +
  • Bonus 2: +Možná sis všiml/a „skoku”, když +raketa vyletí z okýnka a vrátí se na druhé straně. +Tomu se dá zabránit tak, že +vlevo, vpravo, nahoře i dole vedle naší „scény” +vykreslíš celou scénu ještě jednou.

    +

    Pyglet na to má speciální nízkoúrovňové funkce, +kterými můžeš říct „tady kresli všechno posunuté o +X pixelů vlevo”. Úplné vysvětlení by bylo na dlouho, +takže si zatím jen zkopíruj kód:

    +
    from pyglet import gl
    +
    +def draw():
    +    window.clear()
    +
    +    for x_offset in (-window.width, window.width, 0):
    +        for y_offset in (-window.height, window.height, 0):
    +            # Set the view matrix to offset draws
    +            matrix = pyglet.math.Mat4.from_translation(x_offset, y_offset, 0)
    +            window.view = matrix
    +

    Pro přehled, dokumentace k použitým funkcím je tady: +window.view, + Mat4.

    +
  • +
+

Povedlo se? Můžeš létat vesmírem? +Čas to všechno dát do Gitu!

+

Projdi si předchozí body, jestli máš opravdu všechno, a můžeš pokračovat dál!

+

Asteroidy +# +

+

Přidej druhý typ vesmírného objektu: Asteroid.

+
    +
  • Asteroidy a vesmírné lodě mají mnoho společného: +každý takový vesmírný objekt bude mít polohu, +rychlost, natočení a pravidla, jak se pohybuje. +Vytvoř proto třídu SpaceObject, +ve které bude všechno to společné, a z ní poděď +třídu Spaceship, ve které zůstane +kód specifický pro vesmírnou loď (t.j. ovládání +klávesnicí, obrázek lodě, začátek v prostředku +obrazovky).
  • +
  • Část kódu pro pohyb bude společná pro všechny +vesmírné objekty (např. věci kolem zrychlení); +část bude specifická jen pro raketku (ovládání +pomocí klávesnice). +Využij funkci super() z lekce o dědičnosti.
  • +
  • Napiš ještě třídu Asteroid, +která taky dědí ze SpaceObject, +ale má svoje vlastní chování: +začíná buď na levé nebo spodní straně obrazovky +s náhodnou rychlostí +a ke každému asteroidu se přiřadí +náhodně vybraný obrázek. +(V Asteroidech je levý a pravý okraj v podstatě +to samé; a stejně tak horní a spodní.)
  • +
  • A pak pár asterojdíků různých velikostí přidej +na začátku do hry.
  • +
+

Povedlo se? Máš dva typy objektů? +Čas to všechno dát do Gitu!

+

Zase si projdi, jestli máš všechno hotové, +a jdeme na další část!

+

Kolize +# +

+

Naše asteroidy jsou zatím docela neškodné. Pojďme to změnit.

+
    +
  • V této sekci bude tvým úkolem zjistit, kdy +loď narazila do asteroidu. +Pro zjednodušení si každý objekt nahradíme +kolečkem a budeme počítat, kdy se srazí kolečka. +Každý objekt bude potřebovat mít poloměr – atribut radius.

    +
  • +
  • Aby bylo vidět co si hra o objektech „myslí”, +nakresli si nad každým objektem příslušné kolečko. +Pyglet na to má třídu Circle v modulu +pyglet.shapes:

    +
    def draw_circle(x, y, radius):
    +    circle = shapes.Circle(x=x, y=y, radius=radius)
    +    circle.opacity = 120  # (ne)průhlednost, od 0 (průhledné) do 255 (plné)
    +    circle.draw()
    +

    Pro zrychlení můžeš kolečka přidat do batch a vykreslovat společně se +zbytkem vesmíru.

    +

    Až to bude všechno fungovat, můžeš kolečka dát pryč.

    +
  • +
  • Když asteroid narazí do lodi, loď exploduje a zmizí. +Explozi necháme na později, teď je důležité odebrání objektu ze hry. +Dej ho do metody SpaceObject.delete, +protože vyndávat ze hry se dá jakýkoli objekt. +V této metodě musíš objekt jednak odstranit +ze seznamu objects a pak zrušit jeho Sprite, aby se už v rámci +batch nevykresloval.

    +
  • +
  • A jak udělat ono narážení? +V rámci Spaceship.tick projdi +každý objekt, zjisti jestli vzdálenost mezi lodí +a objektem je menší než součet poloměrů +(t.j. narazily do sebe) a pokud ano, +zavolej na objektu metodu hit_by_spaceship.

    +

    Zjišťování vzdálenosti ve hře, kde se +objekty které vyletí ven vrací na druhé straně, +není úplně přímočaré, takže si příslušný kód pro teď jen zkopíruj:

    +
    def distance(a, b, wrap_size):
    +    """Distance in one direction (x or y)"""
    +    result = abs(a - b)
    +    if result > wrap_size / 2:
    +        result = wrap_size - result
    +    return result
    +
    +def overlaps(a, b):
    +    """Returns true iff two space objects overlap"""
    +    distance_squared = (distance(a.x, b.x, window.width) ** 2 +
    +                        distance(a.y, b.y, window.height) ** 2)
    +    max_distance_squared = (a.radius + b.radius) ** 2
    +    return distance_squared < max_distance_squared
    +

    Většina objektů v dokončené hře (např. oheň z +rakety, střela) nebude při kolizi s lodí dělat nic, +takže metoda SpaceObject.hit_by_spaceship +by neměla dělat nic (musí jen existovat). +Jen asteroid loď rozbije, takže předefinuj +Asteroid.hit_by_spaceship, aby +zavolala delete lodi.

    +

    Protože lodí může být v naší hře obecně více, musí asteroid +vědět, se kterou lodí se srazil, aby ji mohl rozbít. +Metoda hit_by_spaceship by tedy na to měla mít argument.

    +
  • +
+

Povedlo se? Konečně se dá prohrát? +Čas to všechno zkontrolovat, dát do Gitu a můžeme pokračovat!

+

Útok +# +

+

Teď zkusíme asteroidy rozbíjet.

+
    +
  • Raketka umí jednou za 0,3 s vystřelit laser. +Ulož si pro každou raketku (jako atribut) číslo, +které po každém výstřelu nastav na 0,3 +a pak ho v metodě tick nech klesat o 1 za vteřinu. +Když bude záporné, může hráč vystřelit znovu.

    +
  • +
  • Když hráč drží mezerník a může vystřelit, vystřelí. +Ve hře se to projeví tak, že se přidá objekt nové třídy Laser. +Začne na souřadnicích raketky, s natočením raketky +a s rychlostí raketky plus něco navíc ve směru natočení.

    +
  • +
  • Každý objekt třídy Laser si „pamatuje“, +jak dlouho ještě bude ve hře. +Na začátku se tohle číslo nastaví tak, aby přeletěl +zhruba něco víc než jednu obrazovku. +Když dojde čas, Laser zmizí.

    +
  • +
  • Ve své metodě tick laser projde +všechny objekty, a pokud se s některým překrývá, +tak na něm zavolá metodu hit_by_laser. +U většiny objektů tahle metoda nedělá nic, +jen asteroidy bude rozbíjet.

    +
  • +
  • Když se laser dotkne asteroidu, asteroid se +rozdělí na dva menší (nebo, je-li už příliš malý, zmizí úplně).

    +

    Rychlosti nových asteroidů si můžeš nastavit +podle sebe – důležité je jen, aby každý menší +asteroid letěl jinam. +Většinou bývají nové asteroidy rychlejší než ten původní.

    +
  • +
  • A to je vše! Máš funkční hru!

    +
  • +
+

Povedlo se? Dá se i vyhrát? Čas to všechno dát do Gitu!

+

Dokončení a rozšíření +# +

+

Chceš-li ve hře pokračovat, tady jsou další nápady. +Můžeš je dělat v jakémkoli pořadí – nebo si vymysli +vlastní rozšíření!

+
    +
  • Je hra příliš těžká?

    +

    Můžeš přidat životy: na začátku jsou tři, +a dokud nějaký zbývá, raketka se po zásahu +asteroidem objeví znovu uprostřed, +s nulovou rychlostí. +Hra by taky při tomto „restartu” měla ignorovat +držené klávesy, dokud je hráč znovu nezmáčkne +(nejlépe pomocí pressed_keys.clear()).

    +

    Počet náhradních lodí můžeš ukázat ikonkami +na spodku obrazovky.

    +

    Bonus: Několik vteřin po +„restartu” může být raketka nezničitelná, +aby měla čas odletět, když je zrovna uprostřed +okýnka asteroid.

    +
  • +
  • Je hra příliš lehká?

    +

    Přidej úrovně: až hráč vystřílí všechny asteroidy, +postoupí na další úroveň, kde je asteroidů víc než v té předchozí.

    +

    Číslo úrovně můžeš ukázat pomocí +pyglet.text.Label.

    +
  • +
  • Je pozadí příliš černé?

    +

    V sadě obrázků v adresáři Backgrounds +si vyber pozadí, a vytapetuj s ním celý vesmír.

    +
  • +
  • Je hra moc strohá?

    +

    Přidej oheň a exploze! +Chovají se podobně jako Laser, +jen nic neničí a můžou třeba měnit barvu podle +toho, jak dlouho už jsou ve hře.

    +

    Na efekty můžeš použít obrázky +„Smoke particle assets”, +které nakreslil opět Kenney Vleugels. +Doporučuji „White Puff”, které můžeš zmenšit +(např. sprite.scale = 1/10), +přibarvit +(např. sprite.color = 255, 100, 0) +nebo částečně zprůhlednit +(např. sprite.opacity = 100).

    +

    Doporučuji si na efekty udělat nový Batch +a vykreslit ho před tím hlavním, aby efekty +nepřekrývaly herní objekty.

    +
  • +
  • Nepoznáš, kdy jsi prohrál/a nebo vyhrál/a?

    +

    Na konci můžeš ukázat veliký nápis GAME OVER nebo WINNER.

    +
  • +
  • Nudíš se?

    +

    V původní hře se občas objeví UFO, které občas +vystřelí na místo, kde je právě hráčova raketka, +takže pokud hráč stojí pořád na jednom místě a +jenom se točí dokola, UFO ho sestřelí. +Můžeš zkusit dodělat třídu Ufo +a z Laser podědit ShipLaser a UfoLaser.

    +
  • +
+

Povedlo se? Vypadá to a chová se to profesionálně? +Čas to všechno dát do Gitu!

\ No newline at end of file diff --git a/lessons/projects/asteroids/static/screenshot.png b/lessons/asteroids/screenshot.png similarity index 100% rename from lessons/projects/asteroids/static/screenshot.png rename to lessons/asteroids/screenshot.png diff --git a/lessons/async/index.html b/lessons/async/index.html new file mode 100644 index 00000000..bc539019 --- /dev/null +++ b/lessons/async/index.html @@ -0,0 +1,407 @@ +

Ve cvičení použijeme ukázku z PyQt5. +Máte-li ještě virtualenv s nainstalovaným PyQt, použijte ho, případně ho +podle lekce o PyQt nainstalujte znovu.

+

K PyQt si přiinstalujte knihovnu asyncqt:

+
$ python -m pip install asyncqt
+

Nejde-li to, nevadí – nezbytné dnes PyQt nebude.

+

Navíc si nainstalujte knihovnu aiohttp:

+
$ python -m pip install aiohttp
+

+

V minulosti byly na této stránce popsány i generátory. +Neovládáte-li je ještě, přečtěte si o nich.

+

+

AsyncIO +# +

+

Pojďme si povídat o souběžnosti – možnostech, jak nechat počítač dělat víc +úloh věcí najednou.

+

Jak jsme si řekli v lekci o C API, Python má globální zámek, takže pythonní kód +může běžet jen v jednom vlákně najednou. +Taky jsme si řekli, že to většinou příliš nevadí: typický síťový nebo GUI program +stráví hodně času čekáním na události (odpověď z internetu, kliknutí myší atp.) +a u tohoto čekání není potřeba držet zámek zamčený.

+

Servery typicky při zpracovávání požadavku stráví většinu času síťovou komunikací. +Proto se často spouští několik vláken nebo přímo procesů najednou, aby se mohl vytížit +procesor. +Při velkém množství vláken ale nastanou dva problémy. +První je, že vláken nemůže být neomezeně mnoho. +Každé vlákno potřebuje vlastní stack, tj. poměrně velkou část paměti; a počet vláken +bývá omezen i jinak (na Linuxu je globální limit počtu procesů, do kterého se počítají +i jednotlivá vlákna – viz cat /proc/sys/kernel/threads-max). +Druhý problém je, že přepnutí z jednoho vlákna do druhého se může stát kdykoli. +Ověřit si, že je na to program připravený, je poměrně složité a na zajištění +správné funkčnosti je potřeba zamykání či jiné techniky. Ty bývají relativně +pomalé, a tak se jim programátoři snaží vyhnout. +A chyby vzniklé nesprávným ošetřením přepínání vláken bývají složité na odhalení +a vyřešení.

+

Vlákna jsou příklad preemptivního multitaskingu, kdy operační systém rozhoduje, +kdy přepne z jednoho vlákna do druhého, a tuto změnu si prakticky vynutí. +Jednotlivá vlákna se s tím musí vyrovnat. +Alternativou je kooperativní multitasking, kdy se jednotlivé úlohy umí samy vzdát +procesorového času, když např. čekají na síťovou komunikaci. +Programátor tak ví, že dokud takto nepředá kontrolu ostatním úlohám, žádná jiná +úloha mu pod rukama nemůže měnit stav procesu. +Na druhou stranu je ale potřeba dostatečně často kontrolu předávat, aby se všechny +úlohy dostaly ke slovu. +Tuto techniku tak nemůže používat operační systém, pod kterým můžou běžet i špatně +napsané programy. V rámci jednoho procesu se to ale dá s úspěchem využít.

+

Souběžnost v Pythonu +# +

+

V Pythonu existovala a existuje řada knihoven, které nám umožňují „dělat více +věcí zároveň“. +Pro preemptivní multitasking jsou tu threading, tedy podpora pro vlákna, +a multiprocessing, tedy způsob jak spustit nový pythonní proces, +ve kterém se provede určitá funkce +(přičemž vstup a výstup se předává serializovaný přes pipes).

+

Další knihovna, kterou lze z PyPI nainstalovat, je greenlet. +Ta nám dává k dispozici tzv. mikro-vlákna, +která se mezi sebou přepínají v rámci jednoho procesu. +Na rozdíl od systémových vláken nepotřebují tolik paměti navíc, ale +stále jde (alespoň z pohledu programátora) o preemptivní strategii: +k přepnutí může dojít kdykoli, +je tedy potřeba zamykat a složitě hledat málo časté chyby.

+

Byly vyvinuty i knihovny pro kooperativní přepínání, založené na tzv. +futures. +Nejznámější jsou Twisted a Tornado. +Obě jsou relativně staré (2002, resp. 2009), ale stále populární.

+

Ačkoli byly Twisted, Tornado a podobné knihovny užitečné, měly zásadní problém +v tom, že každá má jiné API. +Vznikaly tak kolem nich ekosystémy vázané na konkrétní knihovnu: +server napsaný pro Tornado se nedal použít pod Twisted a aplikace +využívající Twisted nemohla využít knihovnu pro Tornado.

+

Jak to vyřešit?

+

Jeden standard +# +

+

xkcd 927

+

Komiks xkcd, © Randall Munroe, CC-BY-NC

+

Podobně jako přístup k různým SQL databázím je v Pythonu standardizovaný +(knihovny pro SQLite, Postgres, MySQL atd. všechny podporují API definované +v PEP 249) nebo je standardizované API webových serverů (WSGI, PEP 3333), +tak vzniklo standardizované API pro kooperativní multitasking. +Toto API je definováno v PEP 3156 a jeho referenční implementace, asyncio, +je ve standardní knihovně Pythonu. +(Ne že by to zabránilo vzniku dalších asynchronních knihoven jako +Curio a Trio. +Ty ovšem spíš experimentují s novými paradigmaty a osvědčené principy se z nich +postupně dostávají do asyncio.)

+

Interně je asyncio postavené na konceptu futures inspirovaných Tornado/Twisted, +ale jeho „hlavní“ API je postavené na coroutines podobných generátorům.

+

Od Pythonu verze 3.5 používá asyncio místo „normálních“ generátorů +speciální syntaxi, která umožňuje kombinovat asynchronní funkce s příkazy +for a with nebo i yield. +Tuto syntaxi použijeme i tady; máte-li starší Python, podívejte se na potřebné změny uvedené níže.

+

Jak vypadá taková asynchronní funkce? +Definuje se pomocí async def místo def, a může používat příkaz await.

+

Ukažme si to na příkladu:

+
import asyncio
+
+async def count(name, interval):
+    """Prints numbers from 0 in regular intervals"""
+    i = 0
+    while True:
+        print(name, 'counts', i)
+        await asyncio.sleep(interval)
+        i += 1
+
+
+loop = asyncio.get_event_loop()
+loop.run_until_complete(count('Counter', 1))
+loop.close()
+

Co se tu děje? +Příkazem await asyncio.sleep(interval) se asynchronní funkce pozastaví +(podobně jako generátor při yield) a předá kontrolu knihovně asyncio +s informací že za daný čas by kontrolu chtěla zase zpátky. +Než daný interval uplyne, asyncio může spouštět jiné úlohy; +po jeho uplynutí pozastavenou funkci „probudí“ a její algoritmus pokračuje dál.

+

Když žádné jiné úlohy neexistují, je pomalé počítání trochu nudné. +Zkuste proto spustit počítadla dvě. +(Detaily funkce ensure_future a příkazu await task vysvětlíme níže.)

+
import asyncio
+
+async def count(name, interval):
+    ...
+
+async def run_two_counters():
+    fast_task = asyncio.ensure_future(count('Fast', 0.3))
+    slow_task = asyncio.ensure_future(count('Slow', 1))
+    await fast_task
+    await slow_task
+
+loop = asyncio.get_event_loop()
+loop.run_until_complete(run_two_counters())
+loop.close()
+

Spouštění a ukončení – poslední tři řádky – je poněkud složité na zápis, +ale typicky to v každém programu potřebujete napsat jen jednou.

+

V Pythonu verze 3.7 a vyšší lze ty tři poslední řádky nahradit jednodušším:

+
asyncio.run(run_two_counters())
+

V Pythonu verze 3.4 a nižší ještě neexistovala klíčová slova async a +await; asynchronní funkce byly implementovány jako generátory. +Ve starších verzích Pythonu bylo potřeba místo:

+
async def ...:
+    await ...
+

psát:

+
@asyncio.coroutine
+def ...:
+    yield from ...
+

Starý způsob zatím funguje i v novějším Pythonu a dokonce se ještě někdy +objeví i v dokumentaci. +Od Pythonu 3.8 je ale deprecated.

+

Event Loop +# +

+

Knihovna asyncio nám dává k dispozici smyčku událostí, která se, podobně jako +app.exec v Qt, stará o plánování jednotlivých úloh. +Smyček událostí může být více. +Tradiční způsob je, že každé vlákno může mít vlastní smyčku událostí, +kterou získáme pomocí asyncio.get_event_loop a pak ji můžeme spustit dvěma +způsoby:

+
    +
  • loop.run_forever() spustí smyčku na tak dlouho, dokud jsou nějaké úlohy +naplánovány (to trochu odporuje názvu, ale většinou se nestává, že by se +úlohy „vyčerpaly“), nebo
  • +
  • loop.run_until_complete(task) – tahle funkce skončí hned, jakmile je hotová +daná úloha, a vrátí její výsledek.
  • +
+

Nakonec je smyčku potřeba uzavřít (loop.close()), což např. dá použitým +knihovnám možnost korektně uzavřít zbylá síťová spojení.

+

Od Pythonu 3.7 můžete použít asyncio.run(task), což vytvoří novou +smyčku událostí, spustí v ní danou úlohu (pomocí run_until_complete) +a zase ji zavře. +Chcete-li ji použít (a tedy psát kód jen pro Python 3.7+), používejte pak všude +místo ensure_future funkci create_task, která vás lépe ochrání před +těžko nalezitelnými chybami.

+

Async funkce a Task +# +

+

Smyčka událostí provádí úlohy a asynchronní funkce.

+

Asynchronní funkce se definují pomocí async def a umožňují použít příkaz +(nebo operátor) await, kterým se provádění funkce pozastaví a kontrola se +předá jiným úlohám do doby, než nastane nějaká událost (např. uplynul časový +intervaluj, jsou dostupná nová data ze socketu…).

+

Pozastavení funguje podobně jako yield u generátoru.

+

Zavoláním asynchronní funkce dostaneme coroutine pozastavenou na začátku +těla funkce:

+
>>> async def demo():
+...     print('Demo')
+...
+>>> coroutine = demo()
+>>> coroutine
+<coroutine object demo at 0x7fda8be22b90>
+

Naplánujeme-li provádění coroutine na smyčce událostí +(např. pomocí run_until_complete), tělo funkce se začne vykonávat:

+
>>> loop = asyncio.get_event_loop()
+>>> result = loop.run_until_complete(coroutine)
+Demo
+

V rámci jedné coroutine pak lze provedení jiné coroutine naplánovat +a počkat na jejich skončení pomocí await. +Jak run_until_complete tak await nám dají k dispozici návratovou hodnotu +příslušné asynchronní funkce.

+
import asyncio
+
+async def add(a, b):
+    await asyncio.sleep(1)  # schedule a "sleep" and wait for it to finish
+    return a + b
+
+async def demo():
+    coroutine = add(2, 3)
+    result = await coroutine  # schedule "add" and wait for it to finish
+    print('The result is:', result)
+
+loop = asyncio.get_event_loop()
+result = loop.run_until_complete(demo())
+loop.close()
+

Nevýhoda čistých coroutines spočívá v tom, že na každé zavolání +takové funkce lze použít jen jeden await. +Výsledek se nikam neukládá, jen se po skončení jednou předá. +Druhý await pro stejné zavolání asynchronní funkce skončí s chybou. +Zkuste si to – v kódu výše přidejte daší řádek s await coroutine:

+
    print('The result is:', (await coroutine))
+

Tenhle problém můžeme vyřešit tak, že asynchronní funkci „zabalíme“ +jako úlohu, Task. +V Pythonu 3.7 se Task tvoří pomocí asyncio.create_task; +pro kompatibilitu se staršími verzemi ale použijeme ekvivalentní +asyncio.ensure_future. +Task se chová stejně jako coroutine – lze použít v await nebo +run_until_complete, ale navíc:

+
    +
  • výsledek je k dispozici kdykoli po ukončení funkce (např. pro druhý await) a
  • +
  • úloha se naplánuje hned po zavolání ensure_future.
  • +
+

Druhou vlastnost je lepší ukázat na příkladu:

+
import asyncio
+
+async def print_and_wait():
+    print('Async function starting')
+    await asyncio.sleep(0.5)
+    print('Async function done')
+    return 'result'
+
+async def demo_coro():
+    coroutine = print_and_wait()
+    await asyncio.sleep(1)
+    print('Awaiting coroutine')
+    print(await coroutine)     # schedule coroutine and wait for it to finish
+
+async def demo_task():
+    task = asyncio.ensure_future(print_and_wait())  # schedule the task
+    await asyncio.sleep(1)
+    print('Awaiting task')
+    print(await task)  # task is finished at this point; retreive its result
+
+
+loop = asyncio.get_event_loop()
+print('Coroutine:')
+result = loop.run_until_complete(demo_coro())
+print('Task:')
+result = loop.run_until_complete(demo_task())
+loop.close()
+

Fan-Out a Fan-In +# +

+

S pomocí asynchronních funkcí můžeme nad našimi programy přemýšlet tak, +jako by to byly „normální“ procedurálně zapsané algoritmy: máme jedno +„vlákno“, které se provádí od začátku do konce, jen na některých místech +(označených await) se provádění přeruší a zatímco náš kód čeká na výsledek +nějaké operace, může se spustit jiný kus kódu. +Funkce, na které je takto potřeba čekat, bývají v dokumentaci patřičně +označeny (v síťovém programování je to většinou čtení ze socketů nebo inicializace +či ukončení serveru).

+

Pomocí ensure_future a await můžeme k tomu dělat něco navíc: +rozdělit běh našeho programu na víc úloh, které se budou vykonávat „souběžně“ – +například autor scraperu chce stáhnout několik stránek najednou +nebo server souběžně odpovídá na několik požadavků. +Tomuto rozdělení se říká fan-out.

+

Opačná operace je fan-in, kdy několik úloh opět spojíme do jedné. +Výše uvedený scraper může počkat, než jsou všechny stránky stažené – +třeba pomocí jednoho await pro každý Task nebo asynchronní funkce +gather. +Poté může pokračovat zpracováním získaných dat.

+

Počkáním na úlohu (pomocí await, gather, run_until_complete atp.) +získáte její návratovou hodnotu. +Ale na všechny úlohy, i na ty, které nic zajímavého nevrací, je důležité počkat. +Neuděláte-li to, bude asyncio vypisovat varovné hlášky:

+
>>> import asyncio
+>>> asyncio.sleep(1)  # no await
+>>> exit()
+sys:1: RuntimeWarning: coroutine 'sleep' was never awaited
+

Toto varování nikdy neignorujte. Kdyby váš program nedělal co má, spolu s +varováním byste ignorovali výjimky v této coroutine.

+

Asynchronní cykly a kontexty +# +

+

Až budete používat některé „asynchronní“ knihovny, setkáte se pravděpodobně se dvěma +novými konstrukcemi: async for a async with.

+

Fungují jako jejich „ne-async“ varianty, jen na začátku a konci každé iterace (resp. +na začátku a konci bloku) můžou přerušit vykonávání funkce – podobně jako await.

+

Typický příklad je u databází: začátek a konec transakce i získávání jednotlivých +řádků pravděpodobně potřebují komunikaci po síti, takže hypotetická databázová +knihovna by se mohla používat nějak takto:

+
async with database.transaction_context():
+    await database.execute('UPDATE ...')
+    async for row in (await database.execute('SELECT ...')):
+        handle(row)
+

A další +# +

+

Nakonec několik tipů, o kterých je dobré vědět.

+

V asyncio najdeme synchronizační mechanismy známé z vláknového programování, např. +Lock a Semaphore – viz dokumentace.

+

Musíme-li použít blokující funkci, která např. komunikuje po síti bez await a která by +tedy zablokovala i všechny ostatní úlohy, můžeme použít +loop.run_in_executor(), a tím danou funkci zavolat ve vlákně nebo podprocesu, ale výsledek zpřístupnit +jako objekt, na který lze počkat pomocí await. +Použití je opět popsáno v dokumentaci.

+

Občas vás při programování s asyncio zaskočí zrádná chyba. +V takových případech je dobré zapnout debug režim pomocí proměnné prostředí PYTHONASYNCIODEBUG=1. +V tomto režimu asyncio upozorňuje na časté chyby, do některých chybových výpisů přidává informaci o tom, +kde aktuální Task vznikl, apod. +Více informací je zase v dokumentaci.

+

Alternativní smyčky událostí +# +

+

Jak bylo zmíněno na začátku, hlavní cíl asyncio je definovat společné rozhraní +pro různé asynchronní knihovny, aby bylo možné např. kombinovat knihovny pro +Tornado se smyčkou událostí v Twisted. +Samotné asyncio je jen jedna z mnoha implementací tohoto rozhraní. +Zajímavá je například knihovna uvloop, která je asi 2-4× rychlejší než asyncio +(ale má závislosti, které se pro součást standardní knihovny nehodí).

+

Další zajímavá implementace je asyncqt, která pod standardním asyncio API používá +smyčku událostí z Qt. +Umožňuje tak efektivně zpracovávat Qt události zároveň s asynchronními funkcemi +známými z asyncio.

+

Event loop z asyncqt je potřeba na začátku programu naimportovat a nastavit +jako hlavní smyčku událostí, a poté ji, místo Qt-ovského app.exec(), spustit. +Jednotlivé asynchronní funkce se pak používají jako v čistém asyncio: +pomocí asyncio.ensure_future, await, atd.

+

Ukázka:

+
import asyncio
+
+from PyQt5 import QtGui, QtWidgets
+from asyncqt import QEventLoop
+
+app = QtWidgets.QApplication([])
+loop = QEventLoop(app)
+asyncio.set_event_loop(loop)
+
+display = QtWidgets.QLCDNumber()
+display.setWindowTitle('Stopwatch')
+
+display.show()
+
+async def update_time():
+    value = 0
+    while True:
+        display.display(value)
+        await asyncio.sleep(1)
+        value += 1
+
+asyncio.ensure_future(update_time())
+
+loop.run_forever()
+

Komunikace +# +

+

Ono io v asyncio naznačuje, že je tato knihovna dělaná především na +vstup a výstup – konkrétně na komunikaci přes síť (případně s jinými procesy).

+

Ke komunikaci používá asyncio tři úrovně abstrakce: Transport, Protocol +a Stream. +V krátkosti si je tu popíšeme; detaily pak najdete v dokumentaci (je pro nás +totiž mnohem důležitější abyste pochopili principy, než abyste uměli konkrétní +API, které lze dohledat v dokumentaci).

+

Transporty a protokoly jsou postaveny na konceptech knihovny Twisted.

+

Transport zajišťuje samotné posílání bajtů mezi počítači (transportní vrstvu), kdežto +Protocol implementuje nějaký aplikační protokol. +Transport většinou nepíšeme sami, použijeme existující. +V asyncio jsou zabudované transporty pro TCP, UDP a SSL. +Protocol je pak použit pro implementaci konkrétních protokolů jako +HTTP, FTP a podobně. +V dokumentaci najdete podrobnější popis včetně příkladů.

+

Kromě toho existuje i „Stream API“ založené na asynchronních funkcích. +Většinou platí, že operace otevření, čtení, flush a zavření Streamu +jsou asynchronní funkce (v dokumentaci označované jako coroutines), a je +tedy nutné je použít s await; oproti tomu zápis asynchronní není – data +se uloží do bufferu a pošlou se, až to bude možné.

+

Typicky ale místo čistého asyncio použijeme existující knihovnu. +Tady je příklad z knihovny aiohttp, která implementuje server a klienta +pro HTTP:

+
import asyncio
+import aiohttp
+
+async def main(url):
+    # Use a a session
+    session = aiohttp.ClientSession()
+    async with session:
+
+        # Get the response (acts somewhat like a file; needs to be closed)
+        async with session.get(url) as response:
+
+            # Fetch the whole text
+            html = await response.text()
+            print(html)
+
+loop = asyncio.get_event_loop()
+loop.run_until_complete(main('http://python.cz'))
+loop.close()
+
\ No newline at end of file diff --git a/lessons/basic-functions/index.html b/lessons/basic-functions/index.html new file mode 100644 index 00000000..e18f7411 --- /dev/null +++ b/lessons/basic-functions/index.html @@ -0,0 +1,162 @@ +

Užitečné funkce +# +

+

Ukažme si pár základních funkcí, které Python nabízí.

+

Tato kapitola ukazuje výběr z nástrojů, které jsou ti v Pythonu k dispozici. +Tvůj cíl není naučit se vše nazpaměť, ale mít přehled o tom, co je zhruba +možné. +Detaily můžeš vždycky dohledat – ať už na taháku (které jsou, na rozdíl od +školy, vždycky povoleny!), v těchto materiálech, nebo v oficiální +dokumentaci či jinde na Internetu.

+

Můžeš si stáhnout i +tahák, +který se rozdává na srazech. +Doporučuji mít ho ze začátku při ruce. +Když narazíš na úkol, se kterým si nevíš rady, projdi si tahák a zamysli se, +která z funkcí by se dala použít.

+

Vstup a výstup +# +

+

Tyhle funkce už známe. +print vypíše nepojmenované argumenty, oddělené mezerou. +Pojmenovaný argument end určuje, co se vypíše na konci (místo přechodu +na nový řádek); +sep udává, co se vypíše mezi jednotlivými argumenty (místo mezery).

+

Příklad opět spusť ze souboru, ne interaktivně:

+
print(1, 'dvě', False)
+print(1, end=' ')
+print(2, 3, 4, sep=', ')
+

Základní funkce pro načtení vstupu, input, +vypíše otázku, počká na text od uživatele a ten vrátí jako řetězec.

+
input('zadej vstup: ')
+

Kontrolní otázky:

+
    +
  • Je input „normální“ funkce, nebo procedura?
  • +
  • Co bere funkce input jako argument?
  • +
  • Jaká je návratová hodnota funkce input?
  • +
+
+

Řešení

+ + +
+ +

Převádění typů +# +

+

Co ale když nechceme pracovat s řetězcem, ale třeba s číslem? +Tady nám pomůže skupina funkcí, které umí převádět čísla na řetězce a zpátky. +Každý ze tří typů (angl. types) proměnných, které zatím známe, +má funkci, která vezme nějakou hodnotu a vrátí podobnou hodnotu „svého“ typu. +Na celá čísla je funkce int (z angl. integer), na reálná čísla je float +(z angl. floating-point), a pro řetězce str (z angl. string).

+
int(x)              # převod na celé číslo
+float(x)            # převod na reálné číslo
+str(x)              # převod na řetězec
+

Příklady:

+
3 == int('3') == int(3.0) == int(3.141) == int(3)
+8.12 == float('8.12') == float(8.12)
+8.0 == float(8) == float('8') == float(8.0)
+'3' == str(3) == str('3')
+'3.141' == str(3.141) == str('3.141')
+

Ne všechny převody jsou možné:

+
int('blablabla')    # chyba!
+float('blablabla')  # chyba!
+int('8.9')          # chyba!
+

…a jak si poradit s chybou, která nastane, +když použiješ špatnou hodnotu, si řekneme později.

+

Převádění a input +# +

+

Převádění typů se často používá při načítání vstupu, třeba takto:

+
cislo = int(input('Zadej číslo: '))
+

Jak Python vyhodnotí tento výraz? +Zadá-li uživatel 42Enter, +funkce input vrátí řetězec'42'. +Ten pak funkce int vezme jako argument, udělá podle něj číslo a to +číslo vrátí:

+
cislo = int(input('Zadej číslo: '))
+      #     ╰─────────┬─────────╯
+cislo = int(        '42'          )
+      # ╰────────────┬────────────╯
+cislo =             42
+

Matematické funkce +# +

+

Matematika je občas potřeba, takže se pojďme +podívat, jak v Pythonu pracovat s čísly.

+

Jedna zajímavá matematická funkce je k dispozici vždy:

+
round(cislo)    # zaokrouhlení
+

Spousta dalších není k dispozici od začátku programu. +Ne každý má rád matematiku, a ne ve všech druzích programu jsou takové operace +potřeba. +Proto musíš předem – typicky na začátku souboru – říct, že je budeš používat. +To se dělá naimportováním z modulu math:

+
from math import sqrt, floor, ceil
+

Naimportované funkce pak můžeš použít:

+
sqrt(cislo)                 # druhá odmocnina
+
+floor(cislo)                # zaokrouhlení dolů
+ceil(cislo)                 # zaokrouhlení nahoru
+

Kdybys potřeboval/a goniometrické funkce jako sinus, jsou k dispozici taky. +Jen pozor na to, že počítají pro úhly v radiánech. +Hodnoty ve stupních je potřeba na radiány převést.

+
from math import sin, cos, tan, degrees, radians
+
+sin(uhel)       # sinus
+cos(uhel)       # kosinus
+tan(uhel)       # tangens
+
+degrees(uhel)   # převod z radiánů na stupně
+radians(uhel)   # převod ze stupňů na radiány
+

Import a pojmenování souborů

+

Při importování je potřeba si dávat pozor na pojmenování souborů: +importuješ-li from math, nesmí se tvůj program jmenovat math.py.

+

Proč? Když Python v adresáři, ze kterého program pouštíš, najde soubor +math.py, bude se snažit importovat sin z něho místo +z předpřipravené sady matematických funkcí.

+

Náhoda +# +

+

Nakonec si ukážeme dvě funkce, které vrací náhodná čísla. +Jsou užitečné třeba pro hry, ve kterých se hází kostkou nebo tahají +náhodné karty.

+

Opět nejsou potřeba tak často a je potřeba je naimportovat. +Tentokrát z modulu random:

+
from random import randrange, uniform
+

Pak už se dají použít:

+
randrange(a, b)   # náhodné celé číslo od a do b-1
+uniform(a, b)     # náhodné reálné číslo od a do b
+

Pozor na to, že randrange(a, b) +nikdy nevrátí samotné b. +Pokud potřebuješ náhodně vybrat ze tří možností, +použij randrange(0, 3), +což vrátí 0, 1, nebo +2:

+
from random import randrange
+
+cislo = randrange(0, 3)  # číslo je 0, 1, nebo 2
+if cislo == 0:
+    print('Kolečko')
+elif cislo == 1:
+    print('Čtvereček')
+else:  # tady musí být číslo 2
+    print('Trojúhelníček')
+

Pamatuj, když importuješ z modulu random, nesmí se tvůj soubor +jmenovat random.py.

+

A další +# +

+

Python dává k dispozici obrovské množství dalších +funkcí a modulů, i když ne všem budeš ze začátku +rozumět. +Všechny jsou – anglicky – popsány v dokumentaci Pythonu, např. +vestavěné funkce, +matematika.

\ No newline at end of file diff --git a/lessons/git/basics/static/diagram.svg b/lessons/basics/diagram.svg similarity index 100% rename from lessons/git/basics/static/diagram.svg rename to lessons/basics/diagram.svg diff --git a/lessons/git/basics/static/dropbox.png b/lessons/basics/dropbox.png similarity index 100% rename from lessons/git/basics/static/dropbox.png rename to lessons/basics/dropbox.png diff --git a/lessons/git/basics/static/gitk.png b/lessons/basics/gitk.png similarity index 100% rename from lessons/git/basics/static/gitk.png rename to lessons/basics/gitk.png diff --git a/lessons/basics/index.html b/lessons/basics/index.html new file mode 100644 index 00000000..cabf3d32 --- /dev/null +++ b/lessons/basics/index.html @@ -0,0 +1,374 @@ +

Git +# +

+

Ať už programuješ nebo píšeš dokumenty, stává se, +že vytvoříš několik verzí. +Tuhle chceš archivovat část, která už není potřeba, +tamhle chceš svoji práci poslat k ohodnocení, +nebo dokonce kolegům, kteří na ni spolupracují. +A když se verze začnou kupit, může být problém se v nich vyznat.

+

Část těchto problémů řeší nástroje jako Dropbox či +Google Drive, se kterými ses možná již setkal/a. +Tam můžeš například sdílet svůj dokument s dalšími +lidmi nebo se můžeš vrátit k dřívější verzi dokumentu, +když něco pokazíš a nemůžeš si vzpomenout, jak to bylo +předtím. Příklad toho, jak to může vypadat, je zde:

+

Verzovací Rozhraní služby Dropbox

+

V tomto rozhraní ale vidíš pouze verze jednoho dokumentu a navíc +nemůžeš tušit, ke které verzi se to vlastně chceš +vrátit. Nevidíš ani čím se jednotlivé verze liší. +Pro větší projekt by byl takový způsob práce +neefektivní.

+

Programátoři proto používají mocnější nástroje na +správu verzí (angl. version control system. VCS). +Asi nejpopulárnější z nich je Git, se kterým +se teď seznámíme.

+

Budeme hodně pracovat s příkazovou řádkou. +Jestli se s ní ještě nekamarádíš, koukni se na +úvod.

+

Nezapomeň: $ na začátku se nepíše; +je tu proto, aby šlo poznat že jde o příkaz.

+

Instalace +# +

+

Popis instalace Gitu najdeš +zde. +Jestli jsi instalaci přeskočil/a, projdi si ji teď.

+

Repozitář +# +

+

Každý projekt, který budeš verzovat, musí mít pro sebe +vyhrazený adresář. +Vytvoř si tedy nový adresář a přepni se do něj (pomocí cd). +Pak vytvoř gitový repozitář (angl. repository) +pomocí příkazu git init:

+
$ git init
+Initialized empty Git repository in .../.git/
+

Na první pohled to vypadá, že se nic nestalo. +Tenhle příkaz totiž vytvořil skrytý adresář +.git, do kterého uložil nějaké informace. +Přesvědč se příkazem ls -a (Linux) nebo dir /a (Windows). +Adresář .git je schovaný proto, že +ho spravuje Git a ty bys v něm neměl/a nic měnit.

+

V repozitáři zatím nic není. +Zkus to ověřit příkazem git status, který +vypisuje informace o stavu repozitáře:

+
$ git status
+On branch master
+
+No commits yet
+
+nothing to commit (create/copy files and use "git add" to track)
+

„On branch master” říká něco o větvích, k tomu se vrátíme později. +„No commits yet” říká, že zatím nemáš uloženou žádnou revizi. +A „nothing to commit” říká, že je adresář +prázdný – nejsou tu žádné soubory k verzování.

+

První revize +# +

+

Teď si zkus do Gitu něco přidat!

+

Vytvoř soubor basnicka.txt a napiš do něj +nějakou básničku. +Měla by mít aspoň pět řádků, ať pak máme s čím pracovat. +Pak zkus znovu git status: Git oznámí, +že v adresáři je soubor, o kterém ještě „neví“.

+ + +
$ git status
+On branch master
+
+No commits yet
+
+Untracked files:
+  (use "git add <file>..." to include in what will be committed)
+	basnicka.txt
+
+nothing added to commit but untracked files present (use "git add" to track)
+

U každého nového souboru musíme Gitu říct, že +chceme jeho obsah sledovat. +Proveď to se svojí básničkou:

+
$ git add basnicka.txt
+

a znovu zkontroluj stav repozitáře:

+
$ git status
+On branch master
+
+No commits yet
+
+Changes to be committed:
+  (use "git rm --cached <file>..." to unstage)
+	new file:   basnicka.txt
+
+

To, co je zelené („changes to be committed“), +se přidá do další revize (angl. commit), +kterou vytvoříš. +Pojď tedy vytvořit revizi:

+
$ git commit
+[master (root-commit) eb0fcd9] První revize
+ 1 file changed, 6 insertions(+)
+ create mode 100644 basnicka.txt
+

Po zadání tohoto příkazu se otevře editor, +do kterého musíš napsat nějaký popisek, +abys věděl/a, co tahle revize obsahuje za změny. +Pro začátek napiš jen První revize. +Předvyplněné řádky začínající # nech být +(nebo vymaž, podle chuti – Git je ignoruje). +Pak soubor ulož a zavři editor.

+

Jak na editory?

+

Na Windows, máš-li +správně nastavený Git, +se použije Poznámkový blok (Notepad) – stačí něco +napsat, uložit (Ctrl+S) a zavřít +(Alt+F4).

+

Na Linuxu a macOS se objeví editor v příkazové řádce, +který se jmenuje Nano. +Pozná se tak, že v dolních dvou řádcích má malou nápovědu. +Něco napiš, pomocí Ctrl+O +soubor ulož, potvrď jméno souboru (Enter) +a pomocí Ctrl+X editor zavři.

+

Nemáš-li Git nastavený podle instrukcí, objeví se přímo +v příkazové řádce Vim – poměrně složitý editor, který +se teď učit nebudeme. Pozná se tak, že úplně +spodní řádek je prázdný. +V takovém případě stiskni +Esc, napiš :q! (dvojtečka, Q, vykřičník) +a potvrď pomocí Enter. +Pak si nastav Git a zkus git commit znovu.

+

Znovu zkus vypsat stav repozitáře:

+
$ git status
+On branch master
+nothing to commit, working tree clean
+

Tenhle krátký výstup znamená, že od poslední revize +se nic nezměnilo. +Což dává smysl – poslední revizi jsi právě vytvořil/a!

+

A co všechno je v téhle první/poslední revizi? +To ti poví příkaz git show:

+
$ git show
+commit eb0fcd9317cbba3d9406ffe2918dfaad667f100f
+Author: Adéla Novotná <adela.novotna@example.cz>
+Date:   Mon May 18 16:18:40 2020 +0200
+
+    První revize
+
+diff --git a/basnicka.txt b/basnicka.txt
+new file mode 100644
+index 0000000..558d133
+--- /dev/null
++++ b/basnicka.txt
+@@ -0,0 +1,6 @@
++Holka modrooká, nesedávej u potoka
++Holka modrooká, nesedávej tam
++
++V potoce je hastrmánek
++Zatahá tě za copánek
++Holka modrooká, nesedávej tam
+

Vidíš unikátní +označení revize, +pomocí kterého se vždy bude dát dostat k této konkrétní +verzi projektu. +Pak je tam jméno autora a datum vytvoření, +popisek +a nakonec shrnutí změn: byl přidán soubor basnicka.txt +s nějakým obsahem.

+

Když je výpis moc dlouhý, můžeš se v něm pohybovat +(, , PgUp, PgDn) +a zpět se dostaneš klávesou Q jako Quit.

+

Kódování ve Windows

+

Pokud výpis nezvládá znaky s diakritikou, zadej před git show příkaz

+
> set LC_ALL=C.UTF-8
+

Tento příkaz nastaví aktuální terminál: když si otevřeš nové okno +s příkazovou řádkou, bude ho potřeba zadat znovu.

+

Druhá revize +# +

+

Udělej v básničce nějakou malou změnu – změň slovo, +uprav interpunkci nebo přidej sloku. +Pak se opět zeptej Gitu na stav repozitáře.

+
$ git status
+On branch master
+Changes not staged for commit:
+  (use "git add <file>..." to update what will be committed)
+  (use "git restore <file>..." to discard changes in working directory)
+	modified:   basnicka.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+

Soubor je opět červený! Něco se v něm změnilo! +Ale co? +Na to nám odpoví příkaz git diff.

+
$ git diff
+diff --git a/basnicka.txt b/basnicka.txt
+index 558d133..24e2384 100644
+--- a/basnicka.txt
++++ b/basnicka.txt
+@@ -1,6 +1,9 @@
+-Holka modrooká, nesedávej u potoka
+-Holka modrooká, nesedávej tam
++Holka modrooká
++Nesedávej u potoka
++Holka modrooká
++Nesedávej tam
+ 
+ V potoce je hastrmánek
+ Zatahá tě za copánek
+-Holka modrooká, nesedávej tam
++Holka modrooká
++Nesedávej tam
+

Změny se ukazují po řádcích. +Červeně, s -, jsou ukázány +odebrané řádky; zeleně s + +řádky přidané.

+

Změnilo-li se na řádku jen jedno slovo nebo i písmeno, +celý řádek se ukáže jako smazaný a zase přidaný. +Dá se to nastavit i jinak, když je potřeba, +ale je dobré si na tento standard zvyknout.

+

Takhle se dá jednoduše zjistit, co se dělo od poslední verze. +Když ti program přestane fungovat (a v poslední uložené +revizi fungoval), použij git diff – +v jedné ze změn musí být chyba!

+

Řádek začínající @@ říká, +kde v souboru změna je (u mě začínal vypsaný kousek +souboru řádkem 1 a měl 6 řádků; v nové verzi je +opět od 1. řádku, ale narostl na 9).

+

Jsi-li se změnami spokojen/a, řekni Gitu, ať je +použije v další revizi:

+
$ git add basnicka.txt
+

A pro úplnost se znovu koukni, co říká +status – co je zelené, přidá se do další +revize.

+
On branch master
+Changes to be committed:
+  (use "git restore --staged <file>..." to unstage)
+	modified:   basnicka.txt
+
+

Než uděláš druhou revizi, ještě řeknu něco o tom, +jak správně psát k revizím popisky. +Na to je totiž úzus, který téměř všichni programátoři +respektují: na prvním řádku je krátké shrnutí změn, +následuje prázdný řádek a pak detailnější popis důvodů +ke změně a případně změny samotné. +Snaž se délku řádků držet do zhruba 70 znaků; +vodítkem můžou být předvyplněné řádky začínající #. +Nemá cenu popisovat, co je jasné ze změn samotných, +zajímavé jsou hlavně širší souvislosti a důvody ke změnám. +Cokoli, co může přijít vhod, až se změny bude snažit někdo pochopit. +(Ten někdo můžeš být klidně ty, za pár měsíců.)

+

Můj popisek bude znít takhle:

+
Rozdělení dlouhých řádků
+
+Verše básně se většinou píšou na jednotlivé řádky. Myslím, že
+takhle se to líp čte. (Ale, co si budeme povídat, hlavní
+důvod je ukázat co dělá git diff.)
+

Nebude-li se ti někdy dařit shrnout změnu +v 70 znacích, zamysli se, jestli neděláš moc velkou +změnu najednou – např. "změna řetězce X +a dopsání nového cyklu Y" by bylo lepší uložit +jako dvě různé revize.

+

Pomocí git commit vytvoř druhou revizi. +Pak ji zkontroluj:

+
$ git show
+commit 1fcd654a331f290616c948d9841fd8d2a34aa6b4
+Author: Adéla Novotná <adela.novotna@example.cz>
+Date:   Mon May 18 16:18:40 2020 +0200
+
+    Rozdělení dlouhých řádků
+
+    Verše básně se většinou píšou na jednotlivé řádky. Myslím, že
+    takhle se to líp čte. (Ale, co si budeme povídat, hlavní
+    důvod je ukázat co dělá git diff.)
+
+diff --git a/basnicka.txt b/basnicka.txt
+index 558d133..24e2384 100644
+--- a/basnicka.txt
++++ b/basnicka.txt
+@@ -1,6 +1,9 @@
+-Holka modrooká, nesedávej u potoka
+-Holka modrooká, nesedávej tam
++Holka modrooká
++Nesedávej u potoka
++Holka modrooká
++Nesedávej tam
+ 
+ V potoce je hastrmánek
+ Zatahá tě za copánek
+-Holka modrooká, nesedávej tam
++Holka modrooká
++Nesedávej tam
+

Diagram +# +

+

Pro lepší pochopení, co dělají jednotlivé příkazy a v jakém +stavu můžou být soubory/změny, přikládám tento diagram:

+

Diagram revizí

+

Log +# +

+

Teď, když máme za sebou první(ch) pár revizí, +si ukážeme několik příkazů, které nám umožní se +v nich orientovat. +První z nich je git log.

+
$ git log
+commit 1fcd654a331f290616c948d9841fd8d2a34aa6b4
+Author: Adéla Novotná <adela.novotna@example.cz>
+Date:   Mon May 18 16:18:40 2020 +0200
+
+    Rozdělení dlouhých řádků
+
+    Verše básně se většinou píšou na jednotlivé řádky. Myslím, že
+    takhle se to líp čte. (Ale, co si budeme povídat, hlavní
+    důvod je ukázat co dělá git diff.)
+
+commit eb0fcd9317cbba3d9406ffe2918dfaad667f100f
+Author: Adéla Novotná <adela.novotna@example.cz>
+Date:   Mon May 18 16:18:40 2020 +0200
+
+    První revize
+

Git log vypíše všechny revize od té nejnovější až po +úplný začátek projektu.

+

Až budeš mít verzí tolik, že se nevejdou najednou +na obrazovku, můžeš se v logu pohybovat pomocí šipek a +PgUp/PgDn. +„Ven“ se dostaneš klávesou q.

+

Je spousta možností jak vypisovat historii pomocí git log. +Všechno je podrobně – možná až moc podrobně – +popsáno v dokumentaci; stačí zadat git help log. +„Ven“ z dokumentace se opět dostaneš klávesou q.

+

Já často používám git log --oneline --graph --decorate --cherry-mark --boundary. +Chceš-li tyhle možnosti studovat, začni v tomto +pořadí a dej si pauzu vždycky, když přestaneš +rozumět. :)

+

Když se na nějakou verzi budeš chtít podívat podrobněji, +napiš git show 5ff0b, kde místo 5ff0b +uveď prvních několik čísel z označení revize.

+

gitk +# +

+

Z příkazové řádky se dá vyčíst všechno potřebné, +ale chce to trochu praxe. +Někdy je přehlednější použít grafické „klikátko“ jménem +gitk, které se dá spustit příkazem +gitk --all:

+
$ gitk --all
+

+

Tenhle program vypadá celkem šeredně (skoro jako by ho +psali programátoři, které místo designu zajímá, co je +„vevnitř“), ale pro naše účely postačí. +Zkus se v něm trochu zorientovat, pak ho zavři, +udělej dalších pár revizí a koukni se na ně přes +git log a gitk --all.

+

Závěr +# +

+

A to je všechno, co z Gitu zatím budeš potřebovat. +Vždycky, když uděláš git add soubor +a git commit, +aktuální verze souborů se uloží a už nejde (jednoduše) +smazat – pokud nesmažeš celý adresář .git. +Jednotlivé verze a změny od posledního uložení, +si umíš i prohlížet.

+

Možná to všechno zní jako zbytečně moc práce. +Máš tak trochu pravdu – naše projekty jsou zatím +dost malé na to, aby se jen pro ně vyplatilo učit Git. +Ale je dobré ho používat už od začátku. +Až bude správa verzí opravdu potřeba, bude se tenhle +trénink hodit.

+

Takže odteď, kdykoliv uděláš v rámci PyLadies funkční +verzi nějakého programu, pomocí git add a git commit si ji ulož do Gitu.

\ No newline at end of file diff --git a/lessons/beginners/and-or/index.md b/lessons/beginners/and-or/index.md deleted file mode 100644 index 19de22e2..00000000 --- a/lessons/beginners/and-or/index.md +++ /dev/null @@ -1,118 +0,0 @@ -{% if var('bonus') %} -> [note] -> Na tuto stránku odkazuje domácí projekt. -{% endif %} - -# *Nebo* anebo *a* - -Vzpomínáš na tabulku operátorů -z [lekce o Porovnávání]( {{ lesson_url('beginners/comparisons')}} )? -Nyní si ji doplníme o další tři operátory, -které se hodí do podmínek: - - - - - - - - - - - - - - - - - - - - - -
SymbolPříkladPopis
andTrue and False
2 < 3 and 5 < 3
„a zároveň“
orTrue or False
2 < 3 or 5 < 3
„a nebo“
notnot False
not 5 < 3 -
„ne“
- -Například, chceš-li zjistit, jestli je kterékoli z dvou čísel záporné, -můžeš napsat: - -```python -a = float(input("Zadej první stranu obdélníka: ")) -b = float(input("Zadej druhou stranu obdélníka: ")) - -if a <= 0 or b <= 0: - print("Délka nemůže být záporná!") -``` - -> [warning] Falešní kamarádi -> -> Pozor na to, že `and` a `or` nejsou anglická slovíčka, ale operátory, -> které spojují logické výrazy. -> Na *obě* strany `and` i `or` patří výraz, jehož hodnota je `True`/`False` -> (například porovnání). -> -> ```python -> if a <= 0 or b <= 0: -> ``` -> -> Může se zdát, že by se to dalo zkrátit a napsat `if a or b <= 0:` – „pokud -> je A nebo B menší než 0“. -> Ale to si Python přeloží na: -> -> ```python -> if (a) or (b <= 0): -> ``` -> -> ... tedy „pokud platí A, a nebo je B menší než 0“. -> A to moc smyslu nedává. -> (Kdy „platí“ celé číslo?) - - -## Šťastná/Bohatá - -Pro příklad použijeme `and` ve vylepšeném programu, který rozdává nejapné rady -do života. -Zkus si ho projít a okomentovat části, které nejsou na první pohled jasné. - -```python -# Tento program rozdává nejapné rady do života. - -print('Odpovídej "ano" nebo "ne".') -stastna_retezec = input('Jsi šťastná? ') -if stastna_retezec == 'ano' or stastna_retezec == 'Ano': - stastna = True -elif stastna_retezec == 'ne' or stastna_retezec == 'Ne': - stastna = False -else: - print('Nerozumím!') - -bohata_retezec = input('Jsi bohatá? ') -if bohata_retezec == 'ano' or bohata_retezec == 'Ano': - bohata = True -elif bohata_retezec == 'ne' or bohata_retezec == 'Ne': - bohata = False -else: - print('Nerozumím!') - -if bohata and stastna: - # Je bohatá a zároveň štǎstná, ta se má. - print('Gratuluji!') -elif bohata: - # Je bohatá, ale není „bohatá a zároveň šťastná“, - # takže musí být jen bohatá. - print('Zkus se víc usmívat.') -elif stastna: - # Tady musí být jen šťastná. - print('Zkus míň utrácet.') -else: - # A tady víme, že není ani šťastná, ani bohatá. - print('To je mi líto.') -``` - -> [note] -> Všimni si, co se stane, když zadáš něco jiného než „ano“ nebo „ne“. -> -> Proměnná `stastna` nebo `bohata` se nenastaví, a když je ji potom -> potřeba použít, program skončí s chybou. -> -> O tom, jak se vypořádat s chybami, si povíme [později]({{ lesson_url('beginners/exceptions') }}). diff --git a/lessons/beginners/and-or/info.yml b/lessons/beginners/and-or/info.yml deleted file mode 100644 index ffa40dbc..00000000 --- a/lessons/beginners/and-or/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Nebo anebo a -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/basic-functions/index.md b/lessons/beginners/basic-functions/index.md deleted file mode 100644 index 3871ccf2..00000000 --- a/lessons/beginners/basic-functions/index.md +++ /dev/null @@ -1,227 +0,0 @@ - -# Užitečné funkce - -Ukažme si pár základních funkcí, které Python nabízí. - -Tato kapitola ukazuje výběr z nástrojů, které jsou ti v Pythonu k dispozici. -Tvůj cíl není naučit se vše nazpaměť, ale mít přehled o tom, co je zhruba -možné. -Detaily můžeš vždycky dohledat – ať už na taháku (které jsou, na rozdíl od -školy, vždycky povoleny!), v těchto materiálech, nebo v oficiální -dokumentaci či jinde na Internetu. - -Můžeš si stáhnout i -tahák, -který se rozdává na srazech. -Doporučuji mít ho ze začátku při ruce. -Když narazíš na úkol, se kterým si nevíš rady, projdi si tahák a zamysli se, -která z funkcí by se dala použít. - - -## Vstup a výstup - -Tyhle funkce už známe. -`print` vypíše nepojmenované argumenty, oddělené mezerou. -Pojmenovaný argument `end` určuje, co se vypíše na konci (místo přechodu -na nový řádek); -`sep` udává, co se vypíše mezi jednotlivými argumenty (místo mezery). - -> [note] -> Příklad opět spusť ze souboru, ne interaktivně: - -```python -print(1, 'dvě', False) -print(1, end=' ') -print(2, 3, 4, sep=', ') -``` - -Základní funkce pro načtení vstupu, `input`, -vypíše otázku, počká na text od uživatele a ten vrátí jako řetězec. - -```python -input('zadej vstup: ') -``` - -Kontrolní otázky: - -* Je `input` „normální“ funkce, nebo procedura? -* Co bere funkce `input` jako argument? -* Jaká je návratová hodnota funkce `input`? - -{% filter solution %} -Funkce `input` vrací hodnotu, se kterou může program dál pracovat. -Zařadil bych ji tedy mezi „normální“ funkce. - -Jako argument bere `input` otázku, na kterou se uživatele zeptá. - -Návratová hodnota funkce `input` je řetězec s odpovědí uživatele. -{% endfilter %} - - - -## Převádění typů - - -Co ale když nechceme pracovat s řetězcem, ale třeba s číslem? -Tady nám pomůže skupina funkcí, které umí převádět čísla na řetězce a zpátky. -Každý ze tří typů (angl. types) proměnných, které zatím známe, -má funkci, která vezme nějakou hodnotu a vrátí podobnou hodnotu „svého“ typu. -Na celá čísla je funkce `int` (z angl. *integer*), na reálná čísla je `float` -(z angl. *floating-point*), a pro řetězce `str` (z angl. *string*). - -```python -int(x) # převod na celé číslo -float(x) # převod na reálné číslo -str(x) # převod na řetězec -``` - -Příklady: - -```python -3 == int('3') == int(3.0) == int(3.141) == int(3) -8.12 == float('8.12') == float(8.12) -8.0 == float(8) == float('8') == float(8.0) -'3' == str(3) == str('3') -'3.141' == str(3.141) == str('3.141') -``` -Ne všechny převody jsou možné: - -```python -int('blablabla') # chyba! -float('blablabla') # chyba! -int('8.9') # chyba! -``` - -…a jak si poradit s chybou, která nastane, -když použiješ špatnou hodnotu, si řekneme později. - -### Převádění a `input` - -Převádění typů se často používá při načítání vstupu, třeba takto: - -```python -cislo = int(input('Zadej číslo: ')) -``` - -Jak Python vyhodnotí tento výraz? -Zadá-li uživatel 42Enter, -funkce `input` vrátí řetězec`'42'`. -Ten pak funkce `int` vezme jako argument, udělá podle něj číslo a to -číslo vrátí: - -```python -cislo = int(input('Zadej číslo: ')) - # ╰─────────┬─────────╯ -cislo = int( '42' ) - # ╰────────────┬────────────╯ -cislo = 42 -``` - - -## Matematické funkce - -Matematika je občas potřeba, takže se pojďme -podívat, jak v Pythonu pracovat s čísly. - -Jedna zajímavá matematická funkce je k dispozici vždy: - -```python -round(cislo) # zaokrouhlení -``` - -Spousta dalších není k dispozici od začátku programu. -Ne každý má rád matematiku, a ne ve všech druzích programu jsou takové operace -potřeba. -Proto musíš předem – typicky na začátku souboru – říct, že je budeš používat. -To se dělá *naimportováním* z modulu `math`: - -```python -from math import sqrt, floor, ceil -``` - -Naimportované funkce pak můžeš použít: - -```python -sqrt(cislo) # druhá odmocnina - -floor(cislo) # zaokrouhlení dolů -ceil(cislo) # zaokrouhlení nahoru -``` - -Kdybys potřeboval{{a}} goniometrické funkce jako sinus, jsou k dispozici taky. -Jen pozor na to, že počítají pro úhly v [radiánech]. -Hodnoty ve stupních je potřeba na radiány převést. - -[radiánech]: https://cs.wikipedia.org/wiki/Radi%C3%A1n - -```python -from math import sin, cos, tan, degrees, radians - -sin(uhel) # sinus -cos(uhel) # kosinus -tan(uhel) # tangens - -degrees(uhel) # převod z radiánů na stupně -radians(uhel) # převod ze stupňů na radiány -``` - -> [warning] Import a pojmenování souborů -> Při importování je potřeba si dávat pozor na pojmenování souborů: -> importuješ-li `from math`, nesmí se tvůj program jmenovat `math.py`. -> -> Proč? Když Python v adresáři, ze kterého program pouštíš, najde soubor -> `math.py`, bude se snažit importovat `sin` z něho místo -> z předpřipravené sady matematických funkcí. - - -## Náhoda - -Nakonec si ukážeme dvě funkce, které vrací náhodná čísla. -Jsou užitečné třeba pro hry, ve kterých se hází kostkou nebo tahají -náhodné karty. - -Opět nejsou potřeba tak často a je potřeba je *naimportovat*. -Tentokrát z modulu `random`: - - -```python -from random import randrange, uniform -``` - -Pak už se dají použít: - -```python -randrange(a, b) # náhodné celé číslo od a do b-1 -uniform(a, b) # náhodné reálné číslo od a do b -``` - -Pozor na to, že randrange(a, b) -nikdy nevrátí samotné b. -Pokud potřebuješ náhodně vybrat ze tří možností, -použij randrange(0, 3), -což vrátí 0, 1, nebo -2: - -```python -from random import randrange - -cislo = randrange(0, 3) # číslo je 0, 1, nebo 2 -if cislo == 0: - print('Kolečko') -elif cislo == 1: - print('Čtvereček') -else: # tady musí být číslo 2 - print('Trojúhelníček') -``` - -> [note] -> Pamatuj, když importuješ z modulu `random`, nesmí se tvůj soubor -> jmenovat `random.py`. - -## A další -Python dává k dispozici obrovské množství dalších -funkcí a modulů, i když ne všem budeš ze začátku -rozumět. -Všechny jsou – anglicky – popsány v dokumentaci Pythonu, např. -vestavěné funkce, -matematika. diff --git a/lessons/beginners/basic-functions/info.yml b/lessons/beginners/basic-functions/info.yml deleted file mode 100644 index 16a03e51..00000000 --- a/lessons/beginners/basic-functions/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Užitečné funkce -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. -license: cc-by-sa-40 diff --git a/lessons/beginners/circular-imports/info.yml b/lessons/beginners/circular-imports/info.yml deleted file mode 100644 index 87eb8813..00000000 --- a/lessons/beginners/circular-imports/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Cyklické importy -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/class/index.md b/lessons/beginners/class/index.md deleted file mode 100644 index 9420b8ea..00000000 --- a/lessons/beginners/class/index.md +++ /dev/null @@ -1,357 +0,0 @@ -# Hodnoty a objekty - -Než se dnes začneme zabývat třídami, -podíváme na objekty. - -Co pro programátory znamená slovo *objekt*? - -V Pythonu je to jednoduché – každá hodnota -(tj. něco, co můžeš uložit do proměnné, vrátit -z funkce nebo třeba seznamu) je objekt. -Některé jazyky (třeba Javascript, C++ nebo Java) mají -i jiné hodnoty než objekty, v některých -jazycích (třeba v C) objekty vůbec nejsou. -Ale v Pythonu mezi hodnotou a objektem není rozdíl, -takže je na jednu stranu trošku složitější pochopit, -v čem spočívá ta „objektovitost“, ale na druhou stranu -to zase není potřeba vědět do detailů. - -Základní vlastnost objektů je to, že obsahují jak data -(informace), tak *chování* – instrukce nebo metody, -které s těmito daty pracují. -Třeba řetězce v Pythonu obsahují jak informace -(nějakou sekvenci znaků), tak užitečné metody jako -`upper` nebo `count`. -Kdyby řetězce nebyly objekty, musel by Python mít -spoustu funkcí jako `str_upper` a `str_count`. -Objekty spojují data a funkčnost dohromady. - -> [note] -> Možná namítneš, že třeba `len` je funkce. -> Je to tak, Python není „stoprocentně“ objektový jazyk. -> Funkce `len` ale funguje i na -> objektech, které s řetězci nemají nic společného. - - -# Třídy - -Data každého objektu jsou specifická pro konkrétní -objekt (`"abc"` obsahuje jiné znaky než -`"def"`), ale funkčnost – metody – bývají -společné pro všechny objekty daného typu. -Třeba řetězcová metoda `count()` by se dala -napsat zhruba jako: - -```python -def count(retezec, znak): - pocet = 0 - for c in retezec: - if c == znak: - pocet = pocet + 1 - return pocet -``` - -… a ačkoliv bude vracet jinou hodnotu pro každý řetězec, -samotná metoda je společná všem řetězcům. - -Tohle společné chování určuje -*typ* (angl. *type*) neboli *třída* (angl. *class*) daného objektu. - -> [note] -> V jiných jazycích než Python 3 můžou slova „typ“ a „třída“ označovat různé -> věci. Pro nás to budou synonyma. - -Typ objektu umí zjistit funkce `type`: - -```pycon ->>> type(0) - ->>> type(True) - ->>> type("abc") - ->>> with open('soubor.txt') as f: -... type(f) -... - -``` - -A co je to ten typ neboli třída? Je to *popis*, jak se všechny objekty -daného typu chovají. - -Například `` obsahuje všechno, co je společné všem celým číslům: -že (a jak) se dají sčítat, jak takové číslo převést na řetězec, a tak dále. - - -## Tvoření objektů třídy - -Většinu tříd jde navíc v Pythonu zavolat, jako by -to byly funkce, a vytvořit tak nový objekt dané třídy: - -```pycon ->>> trida_retezcu = type("abc") ->>> trida_retezcu(8) -'8' ->>> trida_retezcu([1, 2, 3]) -'[1, 2, 3]' -``` - -Chová se to stejně jako funkce `str`! Není to podivné? - -Tady se musím omluvit: -[materiály k funkcím](../functions/) -tak trochu lhaly. Funkce `str`, `int`, `float` apod. totiž vůbec -nejsou funkce – jsou to právě třídy: - -```pycon ->>> str - ->>> type('abcdefgh') - ->>> type('abcdefgh') == str -True -``` - -Ale dají se, podobně jako funkce, zavolat. -Třída tedy většinou obsahuje nejen „popis“, jak se -její objekty budou chovat, ale „umí“ takové objekty i vytvořit. - -## Vlastní třídy - -A proč najednou tolik informací o třídách? -Protože si zkusíme napsat třídu vlastní. - -Třídu se hodí napsat, když plánuješ mít ve svém -programu více objektů s podobným chováním. -Třeba karetní hra by mohla mít třídu Karta, -webová aplikace třídu Uživatel, -tabulkový procesor třídu Řádek. - -My teď potřebujeme napsat program o zvířátkách. -Začni tím, že napíšeš třídu pro koťátka, která umí mňoukat: - -```python -class Kotatko: - def zamnoukej(self): - print("Mňau!") -``` - -Tak jako se funkce definují pomocí `def`, -třídy mají klíčové slovo `class`, -za které napíšeš jméno třídy, dvojtečku a pak odsazené tělo třídy. -Podobně jako `def` dělá funkce, příkaz -`class` udělá novou třídu a přiřadí ji -do proměnné daného jména (tady `Kotatko`). - -Třídy se tradičně pojmenovávají s velkým písmenem, -aby se nepletly s „normálními“ hodnotami. - -> [note] -> Základní třídy (`str`, `int` atd.) -> velká písmena nemají, a to hlavně z historických -> důvodů – původně to byly opravdu funkce. - -V těle třídy můžeš definovat metody, které vypadají -úplně jako funkce – jen mají první parametr `self`. -Ten si ale vysvětlíme později – napřed zkus zamňoukat: - -```python -# Vytvoření konkrétního objektu -mourek = Kotatko() - -# Volání metody -mourek.zamnoukej() -``` - -V tomhle příkladu si dej pozor na velikost písmen: -`Kotatko` (s velkým K) je třída – popis, jak -se koťátka chovají. -`mourek` (s malým m) -je konkrétní objekt (angl. *instance*) té třídy: -hodnota, která reprezentuje kotě. - -Když definuješ třídu (pomocí bloku `class`), neznamená to zatím, že ve tvém -programu je nějaké koťátko. -Třída je jako recept nebo manuál: když si koupíš kuchařku, budeš teoreticky -vědět jak upéct dort, jak bude takový dort vypadat a že se dá sníst. -Ale neznamená to ještě, že máš samotný dort! - -Konkrétní objekt vytvoříš až zavoláním třídy. -Stejně jako zavoláním `str()` se dá vytvořit konkrétní řetězec, -volání `Kotatko()` vytvoří nový objekt tvé třídy, který už můžeš použít. - -Mňau! - -## Atributy - -Objekty vytvořené z „vlastních“ tříd mají funkčnost, kterou třídy jako `str` -nedovolují: máš možnost si definovat vlastní -*atributy* – informace, které se uloží k danému objektu. -Atributy se označují tak, že mezi hodnotu a jméno -jejího atributu napíšeš tečku: - -```python -mourek = Kotatko() -mourek.jmeno = 'Mourek' - -micka = Kotatko() -micka.jmeno = 'Micka' - -print(mourek.jmeno) -print(micka.jmeno) -``` - -Na začátku jsme si řekl{{gnd('i', 'y', both='i')}}, že objekty spojují chování -a data. -Chování je definováno ve třídě; data se schovávají -právě v atributech jednotlivých objektů. -Podle atributů jako `jmeno` pak můžeš jednotlivá koťátka -rozlišit. - -> [note] -> Asi sis všiml{{a}}, že tečkou se dostaneš jak k metodám -> převzaným z třídy, tak k atributům specifickým -> pro konkrétní objekt. -> Co se stane, když má atribut stejné jméno jako -> metoda z třídy? Vyzkoušej si to: -> -> ```python -> micka = Kotatko() -> micka.zamnoukej = 12345 -> micka.zamnoukej() -> ``` - -## Parametr `self` - -Teď se na chvíli vraťme k metodám. Konkrétně k parametru `self`. - -Každá metoda má přístup ke konkrétnímu objektu, na -kterém pracuje, právě přes parametr `self`. -Teď, když máš koťátka pojmenovaná, můžeš v metodě `zamnoukej` použít `self` -a dostat se tak ke jménu daného koťátka: - -```python -class Kotatko: - def zamnoukej(self): - print(f"{self.jmeno}: Mňau!") - -mourek = Kotatko() -mourek.jmeno = 'Mourek' - -micka = Kotatko() -micka.jmeno = 'Micka' - -mourek.zamnoukej() -micka.zamnoukej() -``` - -Co se stalo? Výraz `mourek.zamnoukej` udělá *metodu*. -Když ji pak zavoláš (`mourek.zamnoukej()`), -objekt `mourek` se předá funkci `zamnoukej` jako první argument, `self`. - -> [note] -> Onen první parametr metody můžeš teoreticky pojmenovat i jinak než `self`, -> ale když to uděláš, ostatní programátoři se na tebe budou koukat hodně divně. - -Metoda může mít po `self` i další parametry. -Při volání `self` vynecháš – doplní se vždy automaticky – ale ostatní -se předávají jako u normálního volání funkce. -Třeba v tomto příkladu se jako `jidlo` předá řetězec `'ryba'`: - -```python -class Kotatko: - def zamnoukej(self): - print(f"{self.jmeno}: Mňau!") - - def snez(self, jidlo): - print(f"{self.jmeno}: Mňau mňau! {jidlo} mi chutná!") - -mourek = Kotatko() -mourek.jmeno = 'Mourek' -mourek.snez('ryba') -``` - -## Metoda `__init__` - -Co se stane, když koťátku zapomeneš nastavit jméno? -Metoda `zamnoukej` přestane fungovat: - -```pycon ->>> micka = Kotatko() ->>> micka.snez('ryba') -Traceback (most recent call last): - File "", line 5, in snez -AttributeError: 'Kotatko' object has no attribute 'jmeno' -``` - -Aby tahle chyba nemohla nastat, můžeš zařídit, aby každé kotě *muselo* být -pojmenované už od okamžiku, kdy vznikne. -Jméno pak budeš zadávat už při vytváření kotěte, nějak takhle: - -```python -mourek = Kotatko(jmeno='Mourek') -``` - -To ale zatím nefunguje; musíš na to třídu `Kotatko` připravit. - -Použij na to speciální metodu, která se jmenuje `__init__` (dvě podtržítka, -`init`, dvě podtržítka). -To „opodtržítkování“ znamená, že tohle jméno je nějakým způsobem speciální. -Metodu `__init__` totiž Python zavolá -automaticky, když vytvoří nový objekt. -Můžeš tedy napsat: - -```python -class Kotatko: - def __init__(self, jmeno): - self.jmeno = jmeno - - def zamnoukej(self): - print(f"{self.jmeno}: Mňau!") - - def snez(self, jidlo): - print(f"{self.jmeno}: Mňau mňau! {jidlo} mi chutná!") - -mourek = Kotatko('Mourek') -mourek.zamnoukej() -``` - -A teď už není možnost jak vytvořit koťátko beze jména. -Metoda `zamnoukej` bude vždycky fungovat. - -Jako u jiných funkcí je možné jméno koťátka zadat buď jako pojmenovaný -argument, nebo jako poziční. Obojí funguje stejně: - -```python -mourek = Kotatko('Mourek') # 'Mourek' je hodnota prvního argumentu pro __init__ (po self) -micka = Kotatko(jmeno='Micka') # 'Micka' je hodnota argumentu `jmeno` -``` - -### Metoda `__str__` - -Podobných „opodtržítkovaných“ (speciálních) metod je víc. -Třeba metodu `__str__` Python zavolá, když je potřeba -převést objekt na řetězec: - -```python -class Kotatko: - def __init__(self, jmeno): - self.jmeno = jmeno - - def __str__(self): - return f'' - - def zamnoukej(self): - print(f"{self.jmeno}: Mňau!") - - def snez(self, jidlo): - print(f"{self.jmeno}: Mňau mňau! {jidlo} mi chutná!") - -mourek = Kotatko('Mourek') -print(mourek) -``` - - -A to je o samotných třídách zatím vše. -[Příště](../inheritance/) si něco řekneme o dědičnosti. -A o štěňátkách. diff --git a/lessons/beginners/class/info.yml b/lessons/beginners/class/info.yml deleted file mode 100644 index 31ff7178..00000000 --- a/lessons/beginners/class/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Třídy -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2015-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/cmdline/index.md b/lessons/beginners/cmdline/index.md deleted file mode 100644 index 04b7b45c..00000000 --- a/lessons/beginners/cmdline/index.md +++ /dev/null @@ -1,439 +0,0 @@ -{%- macro sidebyside(titles=['Unix', 'Windows']) -%} -
- {%- for title in titles -%} -
-

{{ title }}

-{%- filter markdown() -%} -```{%- if title.lower().startswith('win') -%}dosvenv{%- else -%}console{%- endif -%} -{{ caller() | extract_part(loop.index0, '---') | dedent }} -``` -{%- endfilter -%} -
- {%- endfor -%} -
-{%- endmacro -%} - - -# Příkazová řádka - -V této lekci se seznámíme s *příkazovou řádkou* – černým okýnkem, -které programátoři používají na zadávání textových příkazů. - -Na většinu z toho, co příkazová řádka umí, můžeš použít i něco jiného – ikonku -na ploše, speciální program nebo editor, webovou aplikaci – ale tyhle -vychytávky mají dvě nevýhody: -* často se liší mezi různými počítači, takže s „tvojí“ variantou ti bude moci - pomoct míň lidí, a -* z příkazové řádky se dá jednoduše kopírovat text, což zjednodušuje - spolupráci přes e-mail nebo chat. - -I když příkazová řádka možná není úplně nejjednodušší způsob jak -s programováním začít, -dlouhodobě se ti základy práce s ní určitě vyplatí. -A i o tom tenhle kurz je. - -Příkazová řádka (respektive program, kterému se říká i *konzole* či *terminál*; -anglicky *command line*, *console*, *terminal*) -se na různých systémech otevírá různě: - -* Windows (české): Start → napsat na klávesnici „cmd“ → Příkazový řádek -* Windows (anglické): Start → napsat na klávesnici „cmd“ → Command Prompt -* macOS (anglický): Applications → Utilities → Terminal -* Linux (GNOME, čeština): Činnosti → hledat Terminál -* Linux (GNOME, angličtina): Activities → hledat Terminál -* Linux (KDE): Hlavní Menu → hledat Konsole - -Nevíš-li si rady, zkus buď googlit, nebo se zeptat někoho zkušenějšího. - -Po otevření konzole tě uvítá *výzva* (angl. *prompt*): řádek, -kterým počítač vybízí k zadání příkazu. -Výzva končí na Unixových systémech (např. Linux a macOS) znakem `$`; -na Windows znakem `>`. - -Před tím znakem `$` nebo `>` budou nejspíš ještě nějaké další -informace, které jsou ovšem v těchto materiálech vynechané. -A budou vynechané i ve většině ostatních návodů co najdeš na internetu. -Na každém počítači totiž můžou být trochu jiné. - -{% call sidebyside(titles=['Unix (Linux, macOS)', 'Windows']) %} -$ ---- -> -{% endcall %} - -Podle systému se potom liší i samotné příkazy, které budeš zadávat: -Unixové systémy (Linux a macOS) rozumí jiným příkazům než Windows. - -> [note] Velikost písma -> Je-li ve Windows moc malé písmo, klikni na ikonku okna a vyber Možnosti. -> V záložce Písmo si pak můžeš vybrat větší font. -> -> -> {{ figure( - img=static('windows-cmd-properties.png'), - alt='Screenshot menu příkazové řádky', -) }} -> -> Na ostatních systémech hledej v nastavení, nebo zkus -> Ctrl++ a -> Ctrl+- (příp. se Shift). - - -## První příkaz - -Začněme ale příkazem, který je všude stejný. -Napiš `whoami` (z angl. *who am I?* – kdo jsem?) -a stiskni Enter. -Objeví se přihlašovací jméno. Třeba u Heleny by to vypadalo takhle: - -{% call sidebyside() %} -$ whoami -helena ---- -> whoami -pocitac\Helena -{% endcall %} - -> [note] -> Znak `$` nebo `>` je v ukázce jen proto, aby bylo jasné že zadáváš -> příkaz do příkazové řádky. -> Vypíše ho počítač, většinou ještě s něčím před ním, -> takže ho nepiš {{gnd('sám','sama')}}! Zadej jen `whoami` a Enter. -> -> Stejně tak počítač sám vypíše přihlašovací jméno. - - -## Aktuální adresář - -Příkazová řádka pracuje vždy v nějakém *adresáři* neboli *složce* -(angl. *directory*, *folder*). -Adresář a složka jsou synonyma; můžeš používat kterékoli z nich. - -Ve kterém adresáři zrovna jsi, to ti poví příkaz, který se podle systému -jmenuje `pwd` nebo `cd` (z angl. *print working directory* – vypiš pracovní -adresář, resp. *current directory* – aktuální adresář). - -{% call sidebyside() %} -$ pwd -/home/helena/ ---- -> cd -C:\Users\helena -{% endcall %} - -Aktuální adresář se většinou ukazuje i ve výzvě příkazové řádky, před znakem -`$` nebo `>`. -Ale je dobré `pwd`/`cd` znát, kdyby ses náhodou ztratil{{a}}. -Občas totiž bývá vypsaný zkráceně. -A taky třeba budeš v budoucnu muset pracovat na počítači který před `$` -ukazuje něco jiného. - -Něco jako aktuální adresář možná znáš z grafických programů, -kterými vybíráš soubory: typicky mají v horní (nebo na Macu dolní) -části uvedeno který adresář zrovna ukazují. -Příkazová řádka umí soubory ukazovat taky – ale musíš si o to říct. - - -## Co v tom adresáři je? - -Příkaz `ls` nebo `dir` (z angl. *list* – vyjmenovat, resp. *directory* – adresář) -ti vypíše, co aktuální adresář obsahuje: všechny soubory, -včetně podadresářů, které se v aktuálním adresáři nacházejí. - -{% call sidebyside() %} -$ ls -Applications -Desktop -Downloads -Music -… ---- -> dir - Directory of C:\Users\helena -05/08/2014 07:28 PM Applications -05/08/2014 07:28 PM Desktop -05/08/2014 07:28 PM Downloads -05/08/2014 07:28 PM Music -… -{% endcall %} - - -## Změna aktuálního adresáře - -Aktuální adresář se dá změnit pomocí příkazu `cd` -(z angl. *change directory* – změnit adresář). -Za `cd` se píše mezera a jméno adresáře, kam chceš přejít. -Pokud máš adresář `Desktop` nebo `Plocha`, přejdi tam. -Pak nezapomeň ověřit, že jsi na správném místě. - -Používáš-li Linux nebo macOS, dej si pozor na velikost písmen: na těchto -systémech jsou `Desktop` a `desktop` dvě různá jména. - -Používáš-li Windows, `cd` už jsi používal{{a}} – tento příkaz se chová různě -podle toho, jestli něco napíšeš za něj nebo ne. - -{% call sidebyside() %} -$ cd Desktop -$ pwd -/home/helena/Desktop ---- -> cd Desktop -> cd -C:\Users\helena\Desktop -{% endcall %} - -> [note] Poznámka pro Windows -> Pokud přecházíš do adresáře na jiném disku, -> například `D:` místo `C:`, je potřeba kromě `cd` -> zadat jméno disku s dvojtečkou jako zvláštní příkaz (např. `D:`). - - -## Vytvoření adresáře - -Co takhle si zkusit vytvořit adresář? To se dělá příkazem `mkdir` -(z angl. *make directory* – vytvořit adresář). -Za tento příkaz napiš jméno adresáře, který chceš vytvořit – v našem případě -`zkouska`: - -{% call sidebyside() %} -$ mkdir zkouska ---- -> mkdir zkouska -{% endcall %} - -Vypiš si teď obsah aktuálního adresáře pomocí `ls` nebo `dir`. -Jeden z vypsaných adresářů bude `zkouska`. - -Když je adresář vytvořený, můžeš do něj přejít podobně jako jsi před chvílí -{{gnd('přešel', 'přešla')}} na `Desktop` nebo `Plocha`: - -{% call sidebyside() %} -$ cd zkouska ---- -> cd zkouska -{% endcall %} - - -## V grafickém hledátku - -Často nebudeš pracovat *jenom* s příkazovou řádkou. -Vyplatí se umět aktuální adresář z příkazové řádky otevřít i v jiných -programech. - -Otevři si prohlížeč souborů. -Tenhle program je na každém systému jiný: - -
-
-

Linux

- {{ figure( - img=static('linux-file-browser.png'), - alt='Screenshot programu Nautilus na GNOME', - ) }} -
-
-

macOS

- {{ figure( - img=static('macos-file-browser.png'), - alt='Screenshot programu Finder na macOS', - ) }} -
-
-

Windows

- {{ figure( - img=static('windows-file-browser.png'), - alt='Screenshot průzkumníka na Windows', - ) }} -
-
- -Možná umíš v tomhle programu klikáním „donavigovat“ do adresáře, který je -aktivní v příkazové řádce. -V budoucnu to ale bude složitější, takže bude dobré si vyzkoušet kopírovat text -z příkazové řádky a vložit ho do prohlížeče souborů. - -Bohužel se to dělá na každém systému jinak. -A protože známé zkratky Ctrl+C a -Ctrl+V dělají v příkazové řádce něco jiného než -kopírování, nejspíš se to dělá jinak, než jsi zvykl{{gnd('ý', 'á')}}. - -Nejdřív si pomocí příkazu `cd` nebo `pwd` nech vypsat celé jméno adresáře -`zkouska`: - -{% call sidebyside() %} -$ pwd -/home/helena/Desktop/zkouska ---- -> cd -C:\Users\helena\Desktop\zkouska -{% endcall %} - -### Kopírování z příkazové řádky - -Na **Linuxu** vyber text myší a pak buď: -* pravým tlačítkem myši otevři menu a vyber *Kopírovat* nebo *Copy*, nebo -* zmáčkni Ctrl+Shift+C. - (Pozor, v příkazové řádce musíš použít navíc Shift.) - -Na **macOS** vyber text myší a pak stiskni ⌘ Command+C. - -Na **Windows** v menu příkazové řádky (ikonce vlevo nahoře) vyber -*Edit* → *Mark*, text vyber myší a zkopíruj pomocí Enter. - -### Otevření v prohlížeči souborů - -Na **Linuxu** záleží na programu, který používáš. Buď: -* zmáčkni Ctrl+L a jméno adresáře vlož pomocí - Ctrl+V, nebo -* vyber jméno adresáře v horní části, smaž ho a vlož nové pomocí - Ctrl+V. -V obou případech potvrď pomocí Enter - -Na **macOS** vyber v menu *Go* → *Go to Folder*, vlož jméno adresáře pomocí -⌘ Command+V a potvrď pomocí Enter. - -Na Windows klikni na jméno adresáře v horní části. -Převede se tím na editovatelný text. -Smaž ho a pomocí Ctrl+V místo něj vlož nové jméno. -Potvrď pomocí Enter. - - -Teď se můžeš podívat na Plochu nebo do nějakého grafickém programu na -prohlížení adresářů: zjistíš, že se adresář opravdu vytvořil. - -### Vkládání do příkazové řádky - -Občas si otevřeš soubor v prohlížeči souborů a budeš do něj chtít přejít -v příkazové řádce. -Zkopírování jména adresáře doufám nebude problém; vkládání do příkazové řádky -je ale občas jiné než v ostatních programech: - -* Linux: Ctrl+Shift+V -* macOS: ⌘ Command+V -* Windows: Menu *Edit* → *Paste* - -> [note] -> Pokud jsou ve jménu mezery nebo jiné speciální znaky jako `*#$%^()><;"?`, -> musíš ho v příkazové řádce ještě uzavřít do uvozovek: před a za jméno napiš -> `"`, např: -> -> ```console -> $ cd "můj super adresář" -> ``` -> -> Lepší je ale mezery a zvláštní znaky ve jménech souborů nepoužívat. - - -## Pozorování změn - -Vyzkoušej si, že se v řádce projeví i změny, které na počítači -uděláš jiným způsobem. - -V grafickém prohlížeči, který se „dívá“ na stejný adresář, jako máš aktivní -v příkazové řádce, vytvoř nový soubor nebo adresář. -Pak se pomocí příkazu `ls` nebo `dir` podívej, že se opravdu vytvořil. -Potom ho v grafickém programu smaž – a v příkazové řádce se ujisti, -že je opravdu smazaný. - -Na počítači máš jen jednu sadu souborů, se kterou umí manipulovat jak grafické -programy tak příkazová řádka. - - -## O úroveň nahoru - -A poslední věc: jsi-li teď v adresáři `Desktop/zkouska` (nebo `Plocha/zkouska`, -`Desktop\zkouska` atp.), jak se dostat zpátky do `Desktop`? - -Příkaz `cd Desktop` fungovat nebude: tím bys počítači řekl{{a}}, ať se přepne -do adresáře `Desktop` *v aktuálním adresáři*. -Ale v adresáři `zkouska` žádný `Desktop` není! -Je to naopak: `zkouska` je v `Desktop`. -Odborně řečeno, adresář `Desktop` je *nadřazený* aktuálnímu adresáři. - -Nadřazený adresář má speciální jméno `..`, dvě tečky. -Přejdi do něj zadáním `cd ..` a pak se ujisti, že jsi opravdu v `Desktop`: - -{% call sidebyside() %} -$ cd .. -$ pwd -/home/helena/Desktop ---- -> cd .. -> cd -C:\Users\helena\Desktop -{% endcall %} - -Další `cd ..` by tě přesunulo do dalšího nadřazeného adresáře – v našem -příkladu `helena`. - - -## Konec - -Příkazů existuje samozřejmě daleko víc. - -Když se je naučíš, můžeš z příkazové řádky plnohodnotně ovládat -počítač: vytvářet soubory, mazat je, pouštět programy, měnit nastavení -a podobně. -Vydalo by to ale vydalo na samostatný kurz, a tak tady skončíme. - -Vyzkoušej si ještě jeden příkaz, ten, který příkazovou řádku zavírá: `exit`. - -Příkaz `exit` funguje stejně na všech systémech. -To samé platí pro překvapivě mnoho příkazů (kromě těch základních jako -`cd`, `pwd` a `ls`). -Proto ve zbytku těchto materiálů nebudu používat ukázky dvojmo podle -operačního systému. - -A budu používat unixovskou výzvu `$`. -S touto konvencí se setkáš i ve většině návodů na internetu. -Používáš-li Windows, je dobré si na `$` zvyknout i když ve své -řádce máš místo něj `>`. - -Zkus si tedy, co dělá příkaz `exit`: - -```console -$ exit -``` - -A tím je úvod do příkazové řádky hotový. - - -## Přehled - -Tady je tabulka základních příkazů, se kterými si do začátku vystačíš: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
UnixWindowsPopisPříklad
cd adresářcd adresářzměna adresářecd test
cd ..
pwdcdvýpis aktuálního adresářepwd
cd
lsdirvýpis adresářels
dir
exitexitukončeníexit
- -Další příkazy jako `python` nebo `git` si vysvětlíme až budou potřeba, -po tom, co si je nainstaluješ. diff --git a/lessons/beginners/cmdline/info.yml b/lessons/beginners/cmdline/info.yml deleted file mode 100644 index d1564f92..00000000 --- a/lessons/beginners/cmdline/info.yml +++ /dev/null @@ -1,8 +0,0 @@ -title: Příkazová řádka -style: md -attribution: -- Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -- | - Založeno na tutoriálu [Django Girls]. - [Django Girls]: https://tutorial.djangogirls.org/en/intro_to_command_line/ -license: cc-by-sa-40 diff --git a/lessons/beginners/comparisons/index.md b/lessons/beginners/comparisons/index.md deleted file mode 100644 index 6d87e5e6..00000000 --- a/lessons/beginners/comparisons/index.md +++ /dev/null @@ -1,237 +0,0 @@ -# Porovnávání - -Pamatuješ si ještě, co je to operátor? - -V domácím projektu jsme si ukázal{{ gnd('i', 'y', both='i') }} základní aritmetické operátory. -Přidáme-li jeden další (`//`), jsou to tyhle: - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SymbolPříkladPopis
+, -, *, /1 + 1Základní aritmetika
--5Negace
//; %7 // 2; 7 % 2Dělení se zbytkem (celočíselné dělení); zbytek po dělení
**3 ** 2Umocnění (3 na druhou)
- -Python ale zná i další druhy operátorů. -Důležité jsou operátory *porovnávací*. -Zkus si co dělají! -(Buď z programu pomocí `print`, -nebo pusť `python` z příkazové řádky.) - - - - - - - - - - - - - - - - - - - - - - -
SymbolPříkladPopis
==, !=1 == 1, 1 != 1Je rovno, není rovno
<, >3 < 5, 3 > 5Menší než, větší než
<=, >=3 <= 5, 3 >= 5Menší nebo rovno, větší nebo rovno
- -Hodnoty porovnání jsou takzvané *booleovské* hodnoty -(angl. *boolean*, podle [G. Boolea](http://en.wikipedia.org/wiki/George_Boole)). -V Pythonu je můžeš použít vždycky, když potřebuješ vědět, jestli něco platí -nebo neplatí. -Jsou jenom dvě – buď `True` (pravda), nebo `False` (nepravda). - -Jako všechny hodnoty, `True` a `False` můžeš přiřadit do proměnných: - -```python -pravda = 1 < 3 -print(pravda) - -nepravda = 1 == 3 -print(nepravda) -``` - -> [note] -> Všimni si, že rovnost zjistíš pomocí dvou rovnítek: `3 == 3`. -> Jedno rovnítko přiřazuje do proměnné; dvě rovnítka porovnávají. - -Slova True a False můžeš -v programu použít i přímo, -jen si dej pozor na velikost písmen: - -```python -print(True) -print(False) -``` - -## Podmínky - -Teď oprášíme program na výpočet obvodu a obsahu. - -Otevři si v editoru nový soubor. -Jestli ještě v adresáři, kde máš soubory ke kurzům Pythonu, -nemáš adresář pro tuto lekci (třeba `02`), vytvoř si ho. -Nový soubor ulož do něj pod jménem `if.py`. - -Do souboru pak napiš následující program: - -```python -strana = float(input('Zadej stranu čtverce v centimetrech: ')) -print('Obvod čtverce se stranou', strana, 'je', 4 * strana, 'cm') -print('Obsah čtverce se stranou', strana, 'je', strana * strana, 'cm2') -``` - -Program spusť. Funguje? - -Co se stane, když jako stranu zadáš záporné číslo? -Dává výstup smysl? - -Tady je vidět, jak počítač dělá přesně, co se mu řekne. Nepřemýšlí o významu. -Bylo by dobré uživateli, který zadá záporné číslo, -přímo říct, že zadal blbost. Jak na to? - -Nejdřív zkus nastavit proměnnou, která bude `True`, -když uživatel zadal kladné číslo. - - -{% filter solution %} - Taková proměnná se dá nastavit pomocí tohoto kódu: - - ```python - strana = float(input('Zadej stranu čtverce v centimetrech: ')) - cislo_je_spravne = strana > 0 - ``` -{% endfilter %} - -A nyní řekni počítači, aby se na základě hodnoty této proměnné rozhodl, co má udělat. -K tomu můžeš použít dvojici příkazů `if` (*pokud*) -a `else` (*jinak*): - -```python -strana = float(input('Zadej stranu čtverce v centimetrech: ')) -cislo_je_spravne = strana > 0 - -if cislo_je_spravne: - print('Obvod čtverce se stranou', strana, 'je', 4 * strana, 'cm') - print('Obsah čtverce se stranou', strana, 'je', strana * strana, 'cm2') -else: - print('Strana musí být kladná, jinak z toho nebude čtverec!') - -print('Děkujeme za použití geometrické kalkulačky.') -``` - -Neboli: po `if` následuje *podmínka* (angl. *condition*), -což je výraz, podle kterého se budeme rozhodovat. -Za podmínkou je dvojtečka. -Potom následují příkazy, které se provedou, pokud je podmínka pravdivá. -Všechny jsou odsazeny o čtyři mezery.
- -> [note] -> Čtyři mezery neznamenají, že musíš čtyřikrát zmáčknout mezerník! -> K odsazení použij klávesu Tab, která vloží správný počet mezer. -> (Pokud ne, nemáš správně nastavený editor – podívej se do lekce o instalaci.) -> Pomocí Shift+Tab můžeš odsazení zase zmenšit. -> -> A ani Tab není vždycky potřeba. -> Pokud napíšeš řádek s `if` bez chyby, některé editory za tebe další řádek odsadí automaticky. - -Po téhle části stačí napsat neodsazené `else:`, zase s dvojtečkou na konci, -a odsazené příkazy, které se provedou v opačném případě.
-Potom můžeš psát příkazy, které se provedou vždycky – ty odsazené nebudou, -podmíněná část programu už skončila. - -> [style-note] -> Vzato čistě technicky, odsazení nemusí být o čtyři mezery. -> Může být třeba o dvě nebo o jedenáct, nebo se dokonce dá místo mezer použít -> tabulátor. -> V rámci jednoho bloku musí být ale odsazení vždycky stejné, -> takže když pak na jednom programu spolupracuje více lidí, musí se shodnout. -> No a na čtyřech mezerách se shodla většina Pythonního světa. - - -## Další podmíněné příkazy - -Někdy není `else` vůbec potřeba. -V následujícím programu se nedělá nic navíc, pokud je číslo nenulové: - -```python -cislo = int(input('Zadej číslo, přičtu k němu 3: ')) -if cislo == 0: - print('Jé, to je jednoduché!') -print(cislo, '+ 3 =', cislo + 3) -``` - -Někdy je naopak potřeba podmínek několik, -k čemuž slouží příkaz `elif` – kombinace `else` a `if`. -Dává se „mezi“ bloky `if` a `else`. -Příkazů `elif` může být za jedním `if`-em několik, -ale vždy se provede jen jedna „větev“: -ta první, jejíž podmínka je splněna. - -```python -vek = int(input('Kolik ti je let? ')) -if vek >= 150: - print('A ze kterépak jsi planety?') -elif vek >= 18: - # Tahle větev se např. pro "200" už neprovede. - print('Můžeme nabídnout: víno, cider, nebo vodku.') -elif vek >= 1: - print('Můžeme nabídnout: mléko, čaj, nebo vodu') -elif vek >= 0: - print('Sunar už bohužel došel.') -else: - # Nenastala ani nedna ze situací výše – muselo to být záporné - print('Pro návštěvy z budoucnosti bohužel nemáme nic v nabídce.') -``` - -## Zanořování - -Příkazy `if` se dají *zanořovat* (angl. *nest*). -V odsazeném (podmíněném) bloku kódu může být další `if` s dalším odsazeným -kódem. -Třeba u tohoto programu, který rozdává nejapné rady do života: - -```python -stastna = input('Jsi šťastná?') -bohata = input('Jsi bohatá?') - -if stastna == 'ano': - # Tenhle kus kódu se provede, když je "šťastná" - if bohata == 'ano': - print('Gratuluji!') - else: - print('Zkus míň utrácet.') -else: - # Tenhle kus kódu se provede, když není "šťastná" - if bohata == 'ano': - print('Zkus se víc usmívat!') - else: - print('To je mi líto.') -``` diff --git a/lessons/beginners/comparisons/info.yml b/lessons/beginners/comparisons/info.yml deleted file mode 100644 index a07649cc..00000000 --- a/lessons/beginners/comparisons/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Porovnávání -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/def/index.md b/lessons/beginners/def/index.md deleted file mode 100644 index a571815b..00000000 --- a/lessons/beginners/def/index.md +++ /dev/null @@ -1,195 +0,0 @@ -# Definice funkcí -[Dříve]({{ lesson_url('beginners/functions') }}) jsme -volal{{gnd('i', 'y', both='i')}} funkce, které napsal někdo jiný: - -```python -print('Ahoj světe!') -``` - -Dnes si ukážeme, jak psát funkce vlastní. - - -## K čemu jsou funkce? - -Často se stává, že kód, který dělá nějakou jednoduchou věc, není úplně -jednoduchý. -Jako příklad uvedu nám už známý kód, který v určitém řetězci zamění znak -na dané pozici: - -```python -zacatek = slovo[:pozice] -konec = slovo[pozice + 1:] -nove_slovo = zacatek + novy_znak + konec -``` - -Z takového kódu není na první pohled jasné, co přesně dělá. -Zvlášť když kód použiješ ve složitějším programu. - -Dá se to vyřešit komentářem: ten, kdo bude program číst, si může přečíst, -co to má dělat. Samotný složitější kód pak může ignorovat. - -```python -# Ve slově `slovo` zaměnit znak na pozici `pozice` za `novy_znak`; -# výsledek bude v proměnné `nove_slovo`. -zacatek = slovo[:pozice] -konec = slovo[pozice + 1:] -nove_slovo = zacatek + novy_znak + konec -``` - -Ještě lepší ale bude si vytvořit *funkci*, která tenhle složitější postup -provede. -Jakmile takovou funkci vytvoříš, ve složitějším programu pak můžeš místo kódu -výše psát jen: - -```python -nove_slovo = zamen(slovo, pozice, novy_znak) -``` - -Podobně fungují funkce, které už znáš: můžeš zavolat `print(123)`, aniž bys -potřeboval{{a}} znát jakékoli detaily postupu, kterým se číslo převede na -jednotlivé číslice a ty se pak vykreslí na obrazovce. -Nebo řekneš želvě `forward(100)` a nezatěžuješ se tím, jak si želva „pamatuje“ -svůj aktuální úhel natočení nebo jak se vlastně kreslí čára. - -Funkce umožňuje *pojmenovat* nějaký kousek programu, který se pak dá -použít pomocí jména bez detailních znalostí toho, jak to vevnitř funguje. - - -## Definice funkce - -Protože už znáš `if` a `for`, které mají jednořádkovou hlavičku a odsazené tělo -příkazu, neměl by ti zápis funkce připadat příliš zvláštní: - -```python -def zamen(slovo, pozice, novy_znak): - """V daném slově zamění znak na dané pozici za daný nový znak.""" - zacatek = slovo[:pozice] - konec = slovo[pozice + 1:] - nove_slovo = zacatek + novy_znak + konec - return nove_slovo - -print(zamen('kočka', 1, 'a')) -print(zamen('kačka', 2, 'p')) -``` - -Jak to funguje? - -Funkce se *definuje* příkazem `def`, za nějž napíšeš jméno funkce, -pak do závorky seznam *parametrů*, které funkce bere, a pak dvojtečku. - -Potom následuje odsazené *tělo funkce* – příkazy, které funkce provádí. - -Tělo může začít *dokumentačním řetězcem* (angl. *docstring*), který popisuje, -co funkce dělá. -To může být jakýkoli řetězec, ale tradičně se uvozuje třemi uvozovkami -(i v případě, že je jen jednořádkový). - -Příkazem `return` pak můžeš z funkce *vrátit* nějakou hodnotu. - -Při volání funkce se hodnoty, se kterými funkci -zavoláš, přiřadí jednotlivým parametrům. -Takže když zavoláš třeba `zamen('kočka', 1, 'a')`, -můžeš si představit, že se provede toto: - -```python -# Nastavení proměnných podle zadaných argumentů -slovo = 'kočka' -pozice = 1 -novy_znak = 'a' - -# Samotné tělo funkce -zacatek = slovo[:pozice] -konec = slovo[pozice + 1:] -nove_slovo = zacatek + novy_znak + konec -return nove_slovo -``` - -Už víš, že volání `zamen('kočka', 1, 'a')` je výraz. -Aby ho Python vyhodnotil, udělá celý postup výše a jako hodnotu výrazu dosadí -návratovou hodnotu – tedy to, co následuje po `return`. - -Tělo funkce může mít více příkazů – včetně podmínek, cyklů a podobně. -Následující procedura třeba vypíše skóre daného hráče a k tomu hlášku: - -```python -def napis_hlasku(nazev, skore): - """Popíše skóre. Název má být přivlastňovací přídavné jméno.""" - - print(nazev, 'skóre je', skore) - if skore > 1000: - print('Světový rekord!') - elif skore > 100: - print('Skvělé!') - elif skore > 10: - print('Ucházející.') - elif skore > 1: - print('Aspoň něco') - else: - print('Snad příště.') - -napis_hlasku('Tvoje', 256) -napis_hlasku('Protivníkovo', 5) -``` - -## Cvičení - -Zkus napsat funkci, která vrátí obsah obdélníka daných rozměrů. -Příslušný vzoreček je S = a×b, -kde a a b jsou délky stran. - -Funkci zavolej a výsledek vypiš. - -{% filter solution %} -```python -def obsah_obdelnika(a, b): - return a * b - -print('Obsah obdélníka se stranami 3 cm a 5 cm je', obsah_obdelnika(3, 5), 'cm2') -``` -{% endfilter %} - - -## Vracení ukončuje funkci - -Speciální příkaz `return`, který jde použít jenom ve funkcích, vrátí danou -návratovou hodnotu ven z funkce a zároveň *ukončí* provádění funkce. - -Chová se tedy trochu jako `break`, jen místo cyklu opouští celou funkci. - -Podobně jako `break` se dá použít v případech, kdy potřebuješ od uživatele -dostat odpověď – a opakuješ dotaz tak dlouho, dokud požadovanou odpověď -nedostaneš. -Třeba, chceš-li odpověď „ano“ nebo „ne“: - -* Takhle se zjišťuje odpověď ano (Pravda) nebo ne (Nepravda) na danou *otázku*: - * Pořád dokola: - * Zeptej se na *otázku*; zapamatuj si *odpověď*. - * Je-li odpověď „ano“: - * Výsledek je Pravda. Hotovo; dál nepokračuj. - * Jinak, je-li odpověď „ne“: - * Výsledek je Nepravda. Hotovo; dál nepokračuj. - * Pouč uživatele, ať odpoví „ano“ nebo „ne“. -
*(a zkus to znovu – viz „Pořád dokola“)* - -```python -def ano_nebo_ne(otazka): - """Vrátí True nebo False podle odpovědi uživatele""" - while True: - odpoved = input(otazka) - if odpoved == 'ano': - return True - elif odpoved == 'ne': - return False - - print('Nerozumím! Odpověz "ano" nebo "ne".') - -# Příklad použití -if ano_nebo_ne('Chceš si zahrát hru? '): - print('OK! Ale napřed si ji musíš naprogramovat.') -else: - print('Škoda.') -``` - -> [note] -> Stejně jako `if` nebo `break` je `return` *příkaz*, ne funkce. -> Kolem „své“ hodnoty nepotřebuje závorky. diff --git a/lessons/beginners/def/info.yml b/lessons/beginners/def/info.yml deleted file mode 100644 index 403bc1f6..00000000 --- a/lessons/beginners/def/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Definice funkcí -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/dict-with-list-values/index.md b/lessons/beginners/dict-with-list-values/index.md deleted file mode 100644 index 460b64d8..00000000 --- a/lessons/beginners/dict-with-list-values/index.md +++ /dev/null @@ -1,63 +0,0 @@ -# Více hodnot v jednom záznamu slovníku - -Ke každému klíči může patřit jen jedna hodnota. -Jak bys zařídil{{a}}, aby hodnot bylo víc? - -Řekněme, že bys chtěl{{a}} do slovníku uložit tyto telefonní kontakty: - -* Katka: - * 4925219 -* Jirka: - * 7477058 - * 3251156 -* Verča: - * 1019103 - -Jak na to? -Jednoduchý slovník použít nemůžeš. Pozor na následující zápis! - -```python -kontakty = { - 'Katka': '4925219', - 'Jirka': '7477058', - 'Jirka': '3251156', - 'Verča': '1019103', -} -``` - -Python v tomto případě jednotlivé záznamy uloží postupně, jako kdybys napsal{{a}}: - -```python -kontakty = {} -kontakty['Katka'] = '4925219' -kontakty['Jirka'] = '7477058' -kontakty['Jirka'] = '3251156' -kontakty['Verča'] = '1019103' -``` - -A ve výsledku bude mít `'Jirka'` uložené jen jedno číslo. -Python tohle nenahlásí jako chybu, ačkoli to často není to, co chceš. - -Protože ve slovníku může být každému klíči přiřazena jen jedna hodnota, -bude potřeba více hodnot směstnat do jedné. -Každému kontaktu můžeš přiřadit *seznam* čísel: - -```python -kontakty = { - 'Katka': ['4925219'], - 'Jirka': ['7477058', '3251156'], - 'Verča': ['1019103'], -} -``` - -Výraz jako `kontakty['Katka']` pak označuje seznam. -Pokud se Katka přestěhuje do zahraničí a pořídí si nové číslo, -můžeš napsat: - -```python -kontakty['Katka'].append('+897 3788509') -print(len(kontakty['Katka'])) -print(kontakty['Katka'][-1]) -del kontakty['Katka'][0] -print(kontakty['Katka']) -``` diff --git a/lessons/beginners/dict-with-list-values/info.yml b/lessons/beginners/dict-with-list-values/info.yml deleted file mode 100644 index 3a533721..00000000 --- a/lessons/beginners/dict-with-list-values/info.yml +++ /dev/null @@ -1,5 +0,0 @@ -title: Víc hodnot v jednom záznamu slovníku -style: md -attribution: -- Pro PyLadies CZ napsal Petr Viktorin, 2015-2021. -license: cc-by-sa-40 diff --git a/lessons/beginners/dict/index.md b/lessons/beginners/dict/index.md deleted file mode 100644 index cec5855f..00000000 --- a/lessons/beginners/dict/index.md +++ /dev/null @@ -1,295 +0,0 @@ -# Slovníky - -Další základní datový typ, který si představíme – -po číslech, řetězcích, seznamech a n-ticích – -je *slovník* (angl. *dictionary*, `dict`). - -Představ si překladový slovník, třeba tenhle česko-anglický: - -* **Jablko**: Apple -* **Knoflík**: Button -* **Myš**: Mouse - -Slovník v Pythonu obsahuje *záznamy*. Každý záznam přiřazuje -nějakému *klíči* nějakou *hodnotu*. -V našem příkladu je klíči *Jablko* přiřazena hodnota *Apple*, -klíči *Knoflík* náleží hodnota *Button* -a klíč *Myš* ukazuje na *Mouse*. - -V Pythonu by se takový slovník napsal následovně: - -``` pycon ->>> slovnik = {'Jablko': 'Apple', 'Knoflík': 'Button', 'Myš': 'Mouse'} ->>> slovnik -{'Jablko': 'Apple', 'Knoflík': 'Button', 'Myš': 'Mouse'} -``` -Pozor na všechny ty symboly! -V tomhle slovníku jsou klíče i hodnoty řetězce, takže jsou v uvozovkách. -Každý klíč je od své hodnoty oddělený dvojtečkou; -jednotlivé dvojice jsou od sebe oddělené čárkou. -A celý slovník je uzavřený ve složených závorkách. - -Když budeš chtít v takovém slovníku něco najít, potřebuješ vědět co hledat. -Konkrétně potřebuješ *klíč*. -Ten dáš do hranatých závorek: - -``` pycon ->>> slovnik['Jablko'] -'Apple' -``` - -Je to podobné jako u seznamů, jen v hranatých závorkách není index -(pořadové číslo prvku) nebo rozmezí s dvojtečkou, ale právě klíč. - -> [note] -> Naopak to nejde – slovník neumožňuje podle hodnoty přímo zjistit klíč. -> Na překlad z angličtiny do češtiny bys potřeboval{{a}} druhý slovník. - -## Měnění slovníků - -Co se stane, když klíč ve slovníku není? - -``` pycon ->>> slovnik['Pes'] -Traceback (most recent call last): - File "", line 1, in -KeyError: 'Pes' -``` - -Python si postěžuje na `KeyError` – chybu klíče. - -Podobně jako seznamy se ale slovníky dají měnit. -Nový záznam vytvoříš takhle: - -``` pycon ->>> slovnik['Pes'] = 'Dog' ->>> slovnik -{'Jablko': 'Apple', 'Knoflík': 'Button', 'Myš': 'Mouse', 'Pes': 'Dog'} -``` - -> [note] -> Na rozdíl od překladového slovníku nemusí být Pythonní slovník seřazený -> podle abecedy. -> Není to potřeba, počítač umí rychle vyhledávat i bez seřazení. - -Kdybys potřeboval{{a}} změnit už existující záznam, použij stejný příkaz. -K jednomu klíči může být přiřazena jen jedna hodnota. - -``` pycon ->>> slovnik['Pes'] = 'Power strip' ->>> slovnik -{'Jablko': 'Apple', 'Knoflík': 'Button', 'Myš': 'Mouse', 'Pes': 'Power strip'} -``` - -Chceš-li ze slovníku nějaký záznam smazat, dělá se to podobně jako -u seznamů – příkazem `del`: - -``` pycon ->>> del slovnik['Pes'] ->>> slovnik -{'Jablko': 'Apple', 'Knoflík': 'Button', 'Myš': 'Mouse'} -``` - -Když budeš chtít zjistit, kolik je ve slovníku záznamů, -zeptáš se podobně jako na počet znaků řetězce nebo prvků seznamu. -Použiješ funkci `len()`. - -``` pycon ->>> len(slovnik) -3 -``` - -A pomocí `in` můžeš zjistit, jestli slovník obsahuje daný klíč. -Funguje to opravdu jen pro klíče, ne pro přiřazené hodnoty: - -``` pycon ->>> 'Myš' in slovnik -True ->>> 'Mouse' in slovnik -False -``` - -## Iterace - -Když slovník projdeš cyklem `for`, dostaneš *klíče* jednotlivých záznamů: - -```pycon ->>> for klic in slovnik: -... print(klic) -Jablko -Knoflík -Myš -``` - -Kdybys chtěl{{a}} projít místo klíčů hodnoty, použij metodu `values`, -která vrací iterátor hodnot: - -```pycon ->>> for hodnota in slovnik.values(): -... print(hodnota) -Apple -Button -Mouse -``` - -Většinou ale potřebuješ jak klíče tak hodnoty. -K tomu mají slovníky metodu `items`, která vrací iterátor dvojic. -Často využiješ možnost každou dvojici přímo rozbalit v cyklu `for`, -jako se to dělá se `zip` nebo `enumerate`: - -```pycon ->>> for klic, hodnota in slovnik.items(): -... print('{}: {}'.format(klic, hodnota)) -Jablko: Apple -Knoflík: Button -Myš: Mouse -``` - -V průběhu iterace (tedy v rámci `for` cyklu) nesmíš -do slovníku přidávat záznamy, ani záznamy odebírat: - -```python ->>> for klic in slovnik: -... del slovnik[klic] -Traceback (most recent call last): - File "", line 1, in -RuntimeError: dictionary changed size during iteration -``` - -Hodnoty u už existujících klíčů ale měnit můžeš. - -## Jak udělat slovník - -Slovník se dá vytvořit několika způsoby. -První, pomocí složených závorek, jsi už viděl{{a}}. -Další způsob využívá funkci `dict`. -Ta, ve stylu `str`, `int` či `list`, převede cokoli co jde na slovník. - -Slovník je ovšem dost specifická struktura – -čísla ani většina seznamů na něj převést nejdou. -Můžeš ale na slovník převést *jiný slovník*. -Nový slovník pak žije svým vlastním životem; -můžeš ho měnit nezávisle na tom původním. - - -Druhá věc, která jde převést na slovník, je -*sekvence dvojic* klíč/hodnota – ať už seznam: - -```pycon ->>> data = [(1, 'jedna'), (2, 'dva'), (3, 'tři')] ->>> nazvy_cisel = dict(data) ->>> nazvy_cisel -{1: 'jedna', 2: 'dva', 3: 'tři'} -``` - -nebo jiný iterovatelný objekt: - -```pycon ->>> data = enumerate(['nula', 'jedna', 'dva']) ->>> nazvy_cisel = dict(data) ->>> nazvy_cisel -{0: 'nula', 1: 'jedna', 2: 'dva'} -``` - -A to je vše, co se na slovník dá převést. - -Jako bonus umí funkce `dict` ještě -brát pojmenované argumenty. -Každé jméno argumentu převede na řetězec, -použije ho jako klíč, a přiřadí danou hodnotu: - -```python -popisy_funkci = dict(len='délka', str='řetězec', dict='slovník') -print(popisy_funkci['len']) -``` - -> [note] -> Pozor na to, že v tomhle případě musí být klíče -> pythonní „jména“ – musí být použitelné jako jména proměnných. -> Například takhle nejde zadat jako klíč řetězec -> `"def"` nebo `"propan-butan"`. - - -### Zaplň prázdný slovník - -Nejobecnější způsob vytváření slovníků je podobný tomu, co znáš u seznamů: -vytvoř prázdný slovník a postupně do něj přidávej záznamy, jeden za druhým. - -Řekněme, že máš slovník, který přiřazuje ovoci jeho barvu: - -```python -barvy = { - 'hruška': 'zelená', - 'jablko': 'červená', - 'meloun': 'zelená', - 'švestka': 'modrá', - 'ředkvička': 'červená', - 'zelí': 'zelená', - 'mrkev': 'červená', -} -``` - -Následující kód vytvoří slovník se změněnými barvami: - -```python -barvy_po_tydnu = {} -for ovoce, barva in barvy.items(): - barvy_po_tydnu[ovoce] = 'černo-hnědo-' + barva - -print(barvy_po_tydnu['jablko']) -``` - - -## Typy klíčů a hodnot - -Do slovníku můžeš uložit jakoukoli hodnotu: řetězce, seznamy, nebo čísla. - -```python -uzivatel = {'jméno': 'Amálka', 'velikost nohy': 36, 'oblíbená čísla': [5, 27]} -``` - -Jako *klíč* jde ovšem použít jen hodnoty, podle kterých pak Python umí záznam -rychle najít. -A to nejsou všechny. Řetězce a čísla použít jdou: - -```python -jmena_cisel = {2: 'dva', 3: 'tři'} -``` - -Ale seznamy nebo jiné slovníky ne. -Typy, které se dají použít jako klíč ve slovníku, se technicky označují jako -„*hashovatelné*“ (angl. *hashable*). -Tento termín se objevuje v chybových hláškách: - -```pycon ->>> jmena_seznamu = {[1, 2, 3]: 'čísla', ['a', 'b', 'c']: 'řetězce'} -Traceback (most recent call last): - File "", line 1, in -TypeError: unhashable type: 'list' -``` - -N-tice jsou hashovatelné, pokud obsahují jen hashovatelné hodnoty: - -```pycon ->>> figurky = {('c', 1): 'bílý střelec', ('e', 8): 'černý král'} ->>> figurky['c', 1] -'bílý střelec' -``` - -```pycon ->>> jmena = {([1, 2, 3], [3, 4, 5]): 'dvojice seznamů'} -Traceback (most recent call last): - File "", line 1, in -TypeError: unhashable type: 'list' -``` - - -## A to je zatím ke slovníkům vše - -Chceš-li mít všechny triky, které slovníky umí, -pěkně pohromadě, můžeš si stáhnout -[Slovníkový tahák](https://pyvec.github.io/cheatsheets/dicts/dicts-cs.pdf). - -Kompletní popis slovníků najdeš -v [dokumentaci](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict) -Pythonu. diff --git a/lessons/beginners/dict/info.yml b/lessons/beginners/dict/info.yml deleted file mode 100644 index 77017d2d..00000000 --- a/lessons/beginners/dict/info.yml +++ /dev/null @@ -1,11 +0,0 @@ -title: Slovníky -style: md -attribution: -- Část této kapitoly je založena na materiálech [DjangoGirls](https://djangogirls.org/). -- | - Původní DjangoGirls tutoriál přeložila do češtiny skupina dobrovolníků. - Poděkování patří hlavně: Davidovi (dakf), Kristýně Kumpánové, - Veronice Gabrielové, Tomáši Ehrlichovi, Aničce Jaegerové, - Matějovi Stuchlíkovi, Filipovi Sivákovi a Juraji M. Bezručkovi. -- Pro PyLadies CZ upravil Petr Viktorin, 2015-2021. -license: cc-by-sa-40 diff --git a/lessons/beginners/exceptions/index.md b/lessons/beginners/exceptions/index.md deleted file mode 100644 index fbf667ba..00000000 --- a/lessons/beginners/exceptions/index.md +++ /dev/null @@ -1,234 +0,0 @@ -# Výjimky - -Pojďme si prohloubit znalosti o chybách, neboli odborně o *výjimkách* -(angl. *exceptions*). - -Podívej se na následující funkci: - -```python -def nacti_cislo(): - """Získá od uživatele celé číslo a vrátí ho""" - odpoved = input('Zadej číslo: ') - return int(odpoved) -``` - -Když uživatel nezadá číslice, ale třeba text `cokolada`, -nastane výjimka jménem `ValueError` (chyba hodnoty) a Python vypíše -odpovídající chybovou hlášku: - -```pycon -Traceback (most recent call last): - File "ukazka.py", line 4, in nacti_cislo - cislo = int(odpoved) -ValueError: invalid literal for int() with base 10: 'cokolada' -``` - -Program volá funkci `int()` pro něco, co nedává smysl jako číslo. -Co s tím má chudák funkce `int` dělat? -Není žádná rozumná hodnota, kterou by mohla vrátit. -Převádění tohoto textu na celé číslo nedává smysl. - -Až funkce `nacti_cislo` nejlíp „ví“, co se má stát, když uživatel nezadá -číslice. -Stačí se uživatele zeptat znovu! -Kdybys měl{{a}} funkci, která zjistí, jestli jsou v řetězci jen číslice, -mohlo by to fungovat nějak takhle: - -```python -def nacti_cislo(): - """Získá od uživatele celé číslo a vrátí ho""" - while True: - odpoved = input('Zadej číslo: ') - if obsahuje_jen_cislice(odpoved): - return int(odpoved) # máme výsledek, funkce končí - else: - print('To nebylo číslo!') - # ... a zeptáme se znovu -- cyklus `while` pokračuje -``` - -Kde ale vzít funkci `obsahuje_jen_cislice`? -Nemá smysl ji psát znovu – funkce `int` sama nejlíp pozná, co se dá převést na -číslo a co ne. -A dokonce nám to dá vědět – výjimkou, kterou můžeš *zachytit*. - -> [note] -> Ono „obsahuje_jen_cislice“ v Pythonu existuje. Dokonce několikrát. -> Místo řešení problému to ale spíš ilustruje, v čem problém spočívá: -> * Řetězcová metoda `isnumeric` vrací `True` pokud řetězec obsahuje číslice: -> `'123'.isnumeric()` je pravda; `'abc'.isnumeric()` nepravda. -> Problém je, že funkce `int` potřebuje jeden konkrétní druh číslic: -> pro řetězce jako `'½'` nebo `'௩三๓໓`' (trojky v tamilském, japonském, -> thajském a laoském písmu) platí `isnumeric`, ale `int` si na nich -> vyláme zuby stejně jako na `'abc'`. -> * Řetězcová metoda `isdecimal` vrací `True` pokud řetězec obsahuje arabské -> číslice 0-9. To už je lepší, ale stejně to úplně nesedí: `int` si poradí -> s mezerou na začátku, např. s `' 3'`. Funkce `isdecimal` ale takový řetězec -> odmítne. -> -> Chceš-li zjistit, jestli funkce `int` umí daný řetězec převést na číslo, -> nejlepší je použít přímo funkci `int`. - - -## Ošetření chyby - -Pro zachycení chyby má Python příkaz `try`/`except`. - -```python -def nacti_cislo(): - """Získá od uživatele celé číslo a vrátí ho""" - while True: - odpoved = input('Zadej číslo: ') - try: - return int(odpoved) - except ValueError: - print('To nebylo číslo!') - # ... a zeptáme se znovu -- cyklus `while` pokračuje -``` - -Jak to funguje? -Příkazy v bloku uvozeném příkazem `try` se normálně provádějí, ale když -nastane uvedená výjimka, Python přeskočí zbytek bloku `try` a provede všechno -v bloku `except`. -Pokud výjimka nenastala, přeskočí se celý blok `except`. - - -## Druhy chyb - -A co je to `ValueError`? To je typ chyby. -Podobných typů je spousta. -Všechny jsou popsané [v dokumentaci](https://docs.python.org/3/library/exceptions.html#exception-hierarchy); pro nás jsou (nebo budou) důležité tyto: - -```plain -BaseException - ├── SystemExit vyvolána funkcí exit() - ├── KeyboardInterrupt vyvolána po stisknutí Ctrl+C - ╰── Exception - ├── ArithmeticError - │ ╰── ZeroDivisionError dělení nulou - ├── AssertionError nepovedený příkaz `assert` - ├── AttributeError neexistující atribut/metoda, např. 'abc'.len - ├── ImportError nepovedený import - ├── LookupError - │ ╰── IndexError neexistující index, např. 'abc'[999] - ├── NameError použití neexistujícího jména proměnné - │ ╰── UnboundLocalError použití proměnné, která ještě nebyla nastavená - ├── SyntaxError špatná syntaxe, program je nečitelný/nepoužitelný - │ ╰── IndentationError špatné odsazení - │ ╰── TabError kombinování mezer a tabulátorů v odsazení - ├── TypeError špatný typ, např. len(9) - ╰── ValueError špatná hodnota, např. int('xyz') -``` - -Tohle si není potřeba pamatovat – druh chyby, kterou je potřeba zachytit, -vždy najdeš v příslušné chybové hlášce. - -Když odchytáváš obecnou výjimku, -chytnou se i všechny podřízené typy výjimek – -například `except ArithmeticError:` zachytí i `ZeroDivisionError`. -A `except Exception:` zachytí *všechny* výjimky, které běžně chceš zachytit. - - -## Nechytej je všechny! - -Většinu chyb *není* potřeba ošetřovat. - -Nastane-li *nečekaná* situace, je téměř vždy -mnohem lepší program ukončit, než se snažit -pokračovat dál a počítat se špatnými hodnotami. -Navíc chybový výstup, který Python standardně -připraví, může hodně ulehčit hledání chyby. - -Zachytávej tedy jenom ty chyby, které *očekáváš* – víš přesně, která chyba může -nastat a proč; máš možnost správně zareagovat. - -V našem příkladu to platí pro `ValueError` z funkce `int`: víš, že uživatel -nemusí vždy zadat číslo ve správném formátu, a víš, že správná -reakce na tuhle situaci je problém vysvětlit a zeptat se znovu. - -Co ale dělat, když uživatel chce ukončit program a zmáčkne -Ctrl+C? -Nebo když se mu porouchá klávesnice a selže funkce `input`? -Nejlepší reakce na takovou nečekanou situaci je ukončit program a informovat -uživatele (nebo lépe, programátora), že (a kde) je něco špatně. -Neboli vypsat chybovou hlášku. -A to se stane normálně, bez `try`. - - -## Další přílohy k `try` - -Pro úplnost: kromě `except` existují dva jiné bloky, -které můžeš „přilepit“ k `try`, a to `else` a `finally`. -První se provede, když v `try` bloku -žádná chyba nenastane; druhý se provede vždy – ať -už chyba nastala nebo ne. - -Můžeš taky použít více bloků `except`. Provede se vždy maximálně jeden: -ten první, který danou chybu umí ošetřit. - -```python -try: - neco_udelej() -except ValueError: - print('Tohle se provede, pokud nastane ValueError') -except NameError: - print('Tohle se provede, pokud nastane NameError') -except Exception: - print('Tohle se provede, pokud nastane jiná chyba') - # (kromě SystemExit a KeyboardInterrupt, ty chytat nechceme) -except TypeError: - print('Tohle se neprovede nikdy') - # ("except Exception" výše ošetřuje i TypeError; sem se Python nedostane) -else: - print('Tohle se provede, pokud chyba nenastane') -finally: - print('Tohle se provede vždycky; i pokud v `try` bloku byl např. `return`') -``` - - - -## Vyvolání chyby - -Občas se stane, že výjimku budeš potřebovat vyvolat {{gnd('sám', 'sama')}}. - -Často se to stává, když píšeš nějakou obecnou funkci. -Třeba funkci na výpočet obsahu čtverce. -Co se stane, když někdo zavolá `obsah_ctverce(-5)`? - -* Zadal-li ono `-5` uživatel, je potřeba mu vynadat a zeptat se znovu. -* Naměřil-li `-5` nějaký robotický aparát, je potřeba ho líp zkalibrovat. -* Vyšel-li čtverec se stranou `-5` v nějakém výpočtu, je nejspíš potřeba opravit - chybu v tom výpočtu. - -Samotná funkce `obsah_ctverce` ale „neví“, proč ji někdo volá. -Jejím úkolem je jen něco spočítat. -Měla by být použitelná ve všech případech výše – a v mnoha dalších. - -Když někdo zavolá `obsah_ctverce(-5)`, *neexistuje* správný výsledek, který by -funkce mohla vrátit. -Místo vrácení výsledku musí tato funkce *signalizovat chybu*. -S tou se pak může program, který `obsah_ctverce(-5)` zavolal, -vypořádat – vynadat uživateli, zkalibrovat měřák, nebo, pokud na chybu není -připravený, sám skončit s chybou a upozornit tak programátora, že je něco -špatně. - -Jak na to prakticky? -Chybu můžeš vyvolat pomocí příkazu `raise`. -Za příkaz dáš druh výjimky a pak do závorek nějaký popis toho, co je špatně. - -```python -def obsah_ctverce(strana): - """Vrátí obsah čtverce s danou délkou strany""" - if strana > 0: - return strana ** 2 - else: - raise ValueError(f'Strana musí být kladná, číslo {strana} kladné není!') -``` - -Podobně jako `return` i příkaz `raise` ukončí funkci. -A nejen tu – pokud na tuhle konkrétní chybu není program předem připravený, -ukončí se celý program. - -Ze začátku není u `raise` příliš důležité dumat nad tím, který typ výjimky je -ten správný. -Klidně „střílej od boku“. -`ValueError` bývá často správná volba. diff --git a/lessons/beginners/exceptions/info.yml b/lessons/beginners/exceptions/info.yml deleted file mode 100644 index f829b250..00000000 --- a/lessons/beginners/exceptions/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Výjimky -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/expressions/index.md b/lessons/beginners/expressions/index.md deleted file mode 100644 index 8df1609e..00000000 --- a/lessons/beginners/expressions/index.md +++ /dev/null @@ -1,92 +0,0 @@ -# Vyhodnocování výrazů - -Už víš, že Python se dá použít jako kalkulačka: dokáže spočítat -hodnotu *výrazu* (angl. *expression*) jako `3 * (5 + 2)`. -Jak to ale vlastně dělá? -Jak se vyhodnocují výrazy? - -Pro základní výrazy je to tak, jak to možná znáš ze školy. -U `3 * (5 + 2)` nejdřív spočítáš to, co je v závorkách: `(5 + 2)` je `7`. -Výsledek dosadíš do původního výrazu místo závorky: `3 * 7`. -Stejně fungují výrazy v Pythonu. - -Možná to zní jednoduše, ale protože budeme ten samý postup používat -i na složitější výrazy, hodí se ho umět „rozepsat“: - -```python -vysledek = 3 * (5 + 2) -# ╰──┬──╯ -vysledek = 3 * 7 -# ╰─┬────╯ -vysledek = 21 -``` - -Když Python potřebuje vyhodnotit *proměnnou*, dosadí její hodnotu. -Pokud je zrovna v proměnné a číslo 4, za `a` se dosadí `4`: - -```python -a = 4 -b = 5 - -vysledek = (a + b) / a -# | | | -vysledek = (4 + 5) / 4 -# ╰──┬──╯ -vysledek = 9 / 4 -# ╰────┬─╯ -vysledek = 2.25 -``` - -Funguje to i u složitých výrazů. -Python se složitými výrazy nemá problém. -Jen člověk, který program čte či píše, se v nich může lehce ztratit. -Když opravdu potřebuješ napsat složitý výraz, je dobré jej rozdělit na několik -menších nebo vysvětlit pomocí komentáře. - -Je ale dobré mít povědomí o tom, jak složité výrazy „fungují“, -aby ses jich nemusel{{a}} bát. -Měl{{a}} bys být schopn{{gnd('ý', 'á')}} vysvětlit, co se stane, -když se Pythonu zeptáš, kolik je -b + (b² + -4ac)⁰·⁵ / (2a), abys pak věděl{{a}}, co za -tebe Python dělá. - -```python -a = 2 -b = 5 -c = 3 - - -x = -b + (b ** 2 + 4 * a * c) ** 0.5 / (2 * a) -# | | | | | -x = -5 + (5 ** 2 + 4 * 2 * 3) ** 0.5 / (2 * 2) -# ╰──┬─╯ ╰─┬─╯ ╰──┬──╯ -x = -5 + ( 25 + 8 * 3) ** 0.5 / 4 -# ╰────┬─╯ -x = -5 + ( 25 + 24 ) ** 0.5 / 4 -# ╰───────┬──────────╯ -x = -5 + 49 ** 0.5 / 4 -# ╰──────┬──────────╯ -x = -5 + 7.0 / 4 -# ╰─────────────┬────╯ -x = -5 + 1.75 -# ╰──────────────┬───────────────────╯ -x = -3.25 -``` - -Výrazy se používají na více místech Pythonu než jen v přiřazování -do proměnných. -Třeba podmínka u `if` je taky výraz a vyhodnocuje se stejně jako ostatní -výrazy: - -```python -strana = -5 - -if strana <= 0: - print("Strana musí být kladná!") -``` - -```python -if strana <= 0: -# ╰──────┬──╯ -if True : -``` diff --git a/lessons/beginners/expressions/info.yml b/lessons/beginners/expressions/info.yml deleted file mode 100644 index adc0cba4..00000000 --- a/lessons/beginners/expressions/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Vyhodnocování výrazů -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2019. -license: cc-by-sa-40 diff --git a/lessons/beginners/files/index.md b/lessons/beginners/files/index.md deleted file mode 100644 index aca37ba7..00000000 --- a/lessons/beginners/files/index.md +++ /dev/null @@ -1,169 +0,0 @@ -# Soubory - -Dnes se podíváme na to, jak v Pythonu číst z -(a pak i zapisovat do) souborů. - -Vytvoř si v editoru soubor `basnicka.txt` a napiš do něj libovolnou básničku. -Soubor ulož. - -> [note] -> Na uložení souboru s básničkou doporučuji použít -> stejný editor, jaký používáš na Pythonní programy. -> -> Používáš-li jiný editor než Atom, dej si při ukládání pozor na kódování: -> * Nabízí-li ti editor při ukládání výběr kódování, vyber UTF-8. -> * Je-li k dispozici kódování „UTF-8 bez BOM”, použij to. -> * Pokud musíš použít Notepad, který výše uvedené možnosti nemá, pak v kódu -> níže použij místo `'utf-8'` nestandardní `'utf-8-sig'`. -> -> Ono [`utf-8`] je název standardního kódování. -> Zajišťuje, že se případné emoji nebo znaky s diakritikou do souboru uloží -> tak, aby se daly přečíst i na jiném počítači či operačním systému. -> 🎉 - -[`utf-8`]: https://en.wikipedia.org/wiki/UTF-8 - -Potom napiš tento program: - -```python -soubor = open('basnicka.txt', encoding='utf-8') -obsah = soubor.read() -soubor.close() - -print(obsah) -``` -a spusť ho z adresáře, ve kterém je -`basnicka.txt` (jinými slovy, aktuální adresář musí být ten, který -obsahuje soubor s básničkou). - -Obsah souboru se vypíše! - -Co se tu děje? -Tak jako `int()` vrací čísla a `input()` řetězce, funkce -`open()` vrací hodnotu, která představuje *otevřený soubor*. -Tahle hodnota má vlastní metody. -Tady používáme metodu `read()`, která -najednou přečte celý obsah souboru a vrátí ho jako řetězec. -Nakonec metoda `close()` otevřený soubor zase zavře. - - -## Automatické zavírání souborů - -Soubory se dají přirovnat k ledničce: abys něco -mohl{{a}} z ledničky vzít, nebo dát dovnitř, musíš -ji předtím otevřít a potom zavřít. -Bez zavření to sice na první pohled funguje taky, -ale pravděpodobně potom brzo něco zplesniví. - -Stejně tak je docela důležité soubor zavřít po tom, -co s ním přestaneš pracovat. -Bez zavření to na první pohled funguje, ale složitější programy se můžou dostat -do problémů. -Operační systémy mají limity na počet -současně otevřených souborů, které se nezavíráním -dají snadno překročit. -Na Windows navíc nemůžeš soubor, který je stále -otevřený, otevřít znovu. - -Na korektní zavření souboru ale programátoři často zapomenou. -Proto Python poskytuje příkaz `with`, který soubory zavírá automaticky. -Používá se takhle: - -```python -with open('basnicka.txt', encoding='utf-8') as soubor: - obsah = soubor.read() - -print(obsah) -``` - -Příkaz `with` vezme otevřený soubor (který vrací funkce `open`) -a přiřadí ho do proměnné `soubor`. -Pak následuje odsazený blok kódu, kde se souborem můžeš pracovat – v tomhle -případě pomocí metody `read` přečíst obsah jako řetězec. -Když se Python dostane na konec odsazeného bloku, soubor automaticky zavře. - -V naprosté většině případů je pro otevírání souborů nejlepší použít `with`. - - -## Iterace nad soubory - -Otevřené soubory se, jako např. řetězce či `range`, -dají použít s příkazem `for`. -Tak jako `for i in range` poskytuje za sebou jdoucí čísla a `for c in 'abcd'` -poskytuje jednotlivé znaky řetězce, `for radek in soubor` bude do proměnné -`radek` dávat jednotlivé řádky čtené ze souboru. - -Například můžeš básničku odsadit, -aby se vyjímala v textu: - -```python -print('Slyšela jsem tuto básničku:') -print() - -with open('basnicka.txt', encoding='utf-8') as soubor: - for radek in soubor: - print(' ' + radek) - -print() -print('Jak se ti líbí?') -``` - - -Když to zkusíš, zjistíš, že trochu nesedí -řádkování. Zkusíš vysvětlit, proč tomu tak je? - -{% filter solution %} -Každý řádek končí znakem nového řádku, `'\n'`, -který možná znáš ze [sekce o řetězcích](../str/). -Při procházení souboru Python tento znak nechává na konci řetězce `radek` ¹. -Funkce `print` pak přidá další nový řádek, protože ta na konci -výpisu vždycky odřádkovává – pokud nedostane argument `end=''`. - ---- - -¹ Proč to dělá? Kdyby `'\n'` na konci řádků nebylo, -nedalo by se např. dobře rozlišit, jestli poslední řádek -končí na `'\n'` - -{% endfilter %} - -Ideální způsob, jak odřádkování spravit, je odstranit z konce řetězce -bílé znaky (mezery a nové řádky) pomocí metody `rstrip`: - - -```python -print('Slyšela jsem tuto básničku:') -print() - -with open('basnicka.txt', encoding='utf-8') as soubor: - for radek in soubor: - radek = radek.rstrip() - print(' ' + radek) - -print() -print('Jak se ti líbí?') -``` - - -## Psaní souborů - -> [warning] Pozor! -> Pro Python není problém smazat obsah jakéhokoli souboru. -> Psaní do souborů si zkoušej v adresáři, ve kterém nemáš uložené -> důležité informace! - -Soubory se v Pythonu dají i zapisovat. -Pro zápis soubor otevři s pojmenovaným -argumentem `mode='w'` (z angl. *mode*, mód a *write*, psát). - -Pokud soubor už existuje, otevřením s `mode='w'` se veškerý jeho obsah smaže. -Po zavření tak v souboru bude jen to, co do něj ve svém programu zapíšeš. - -Informace pak do souboru zapiš známou funkcí `print`, -a to s pojmenovaným argumentem `file`: - -```python -with open('druha-basnicka.txt', mode='w', encoding='utf-8') as soubor: - print('Naše staré hodiny', file=soubor) - print('Bijí', 2+2, 'hodiny', file=soubor) -``` diff --git a/lessons/beginners/files/info.yml b/lessons/beginners/files/info.yml deleted file mode 100644 index 4124f28e..00000000 --- a/lessons/beginners/files/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Soubory -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/first-steps/index.md b/lessons/beginners/first-steps/index.md deleted file mode 100644 index f9a1b1b3..00000000 --- a/lessons/beginners/first-steps/index.md +++ /dev/null @@ -1,139 +0,0 @@ -# Interaktivní režim Pythonu - -Chceš-li si začít hrát s Pythonem, otevři *příkazový řádek* a aktivuj virtuální prostředí. -Zkontroluj si, že ti na začátku příkazové řádky svítí `(venv)`. - -Je-li tomu tak, nezbývá než – konečně – pustit Python. K tomu použij příkaz `python`: - -``` console -$ python -Python 3.6.6 (...) -Type "help", "copyright", "credits" or "license" for more information. ->>> -``` - -Příkaz vypíše několik informací. Z prvního řádku se můžeš ujistit, že používáš Python 3. -(Verze by měla být `3.6` nebo vyšší. Vidíš-li číslo jako `2.7.11`, něco je špatně – popros o radu kouče.) -Další řádek je informační: Python má k dispozici návody a informace sám o sobě, -ale jsou psané v angličtině a pro trochu pokročilejší publikum. - -Třemi „zobáčky“ `>>>` pak Python poprosí o instrukce. -Je to jako v příkazové řádce, ale místo příkazů jako `cd` a `mkdir` sem budeš psát příkazy Pythonu. - -Příkazy z příkazové řádky v Pythonu nefungují, -ačkoli okýnko vypadá skoro stejně. -Vyzkoušej si to. Za „zobáčky“ napiš `whoami` a zmáčkni Enter: - -```pycon ->>> whoami -Traceback (most recent call last): - File "", line 1, in -NameError: name 'whoami' is not defined -``` - -Tohle je *chybová hláška*, která se objeví vždycky, -když Python nebude spokojený. -V průběhu kurzu jich uvidíš ještě spoustu, -takže si ji dobře prohlédni, ať ji příště poznáš. - -## První příkaz - -Třemi „zobáčky“ `>>>` Python prosí o instrukce. -Pojď mu nějakou dát! - -Ze začátku použij Pythonu jako kalkulačku. -Za tři zobáčky napiš třeba `2 + 3` a zmáčkni Enter. - -``` pycon ->>> 2 + 3 -5 -``` - -> [note] -> Zobáčky `>>>` i odpověď vypisuje sám Python! -> {{ gnd('sám', 'sama') }} zadej jen číslo a Enter. - -Zobrazila se ti správná odpověď? -Pokud ano, gratuluji! První příkaz v Pythonu máš za sebou. - -Zkusíš i odečítání? - -A jak je to s násobením? -Na kalkulačce bys zadal{{a}} `4 × 5`, což se na klávesnici píše špatně. -Python proto používá symbol `*`. - -``` pycon ->>> 4 * 5 -20 -``` - -Symboly jako `+` a `*` se odborně nazývají *operátory*. - -Operátor pro dělení je `/` – jako u násobení, znak `÷` by se psal špatně. - -Při dělení může vzniknout necelé číslo, třeba dva a půl. -Python používá desetinnou *tečku*, ukáže se tedy `2.5`: - -``` python ->>> 5 / 2 -2.5 -``` - -Z důvodů, do kterých teď nebudeme zabíhat, se při dělení desetinná tečka -objeví, i když vyjde číslo celé: -``` pycon ->>> 4 / 2 -2.0 -``` - -Občas se hodí použít dělení se zbytkem, kdy výsledek zůstane jako celé číslo. -Na to má Python operátory `//` (podíl) a `%` (zbytek): - -``` pycon ->>> 5 // 2 -2 ->>> 5 % 2 -1 -``` - -> [style-note] -> Mezery mezi čísly a znamínkem nejsou nutné: `4*5` i `4 * 5` dělá -> to samé co `4 * 5`. -> Je ale zvykem psát kolem operátoru jednu mezeru z každé strany – tak jako -> v těchto materiálech. -> Kód je pak čitelnější. - - -### Ukončení - -Pokud ses dostal{{a}} až sem, gratuluji! -Python máš nejen nainstalovaný, ale taky ti funguje. -Stačí ho už jen zavřít. -V Pythonu se to dělá pomocí `quit()`, s prázdnými závorkami na konci. - -
->>> quit()
-(venv)$
-
- -Zobáčky `>>>` se změnily na výzvu -příkazové řádky, která začíná `(venv)` a končí `$` nebo `>`. -Teď fungují příkazy jako `whoami` a `cd`, ale příkazy Pythonu -jako `1 + 2` fungovat nebudou, dokud Python opět nepustíš pomocí -příkazu `python`. - -Ukončit virtuální prostředí můžeš příkazem `deactivate` – -tentokrát bez závorek. - -```console -(venv)$ deactivate -``` - -Příkazovou řádku můžeš nakonec zavřít příkazem `exit`. - -```console -$ exit -``` - -Pro cvik si zkus Python znovu spustit – nejdřív otevři příkazovou řádku, -pak aktivuj virtuální prostředí, potom spusť Python samotný. diff --git a/lessons/beginners/first-steps/info.yml b/lessons/beginners/first-steps/info.yml deleted file mode 100644 index f0758390..00000000 --- a/lessons/beginners/first-steps/info.yml +++ /dev/null @@ -1,13 +0,0 @@ -title: První krůčky -style: md -attribution: -- Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. -- Část této kapitoly je založena na materiálech [DjangoGirls](https://djangogirls.org/). -- Část této kapitoly je založena na kurzu [Geek Girls Carrots](https://github.com/ggcarrots/django-carrots). -- | - Původní DjangoGirls tutoriál přeložila do češtiny skupina dobrovolníků. - Poděkování patří hlavně: Davidovi (dakf), Kristýně Kumpánové, - Veronice Gabrielové, Tomáši Ehrlichovi, Aničce Jaegerové, - Matějovi Stuchlíkovi, Filipovi Sivákovi a Juraji M. Bezručkovi. - -license: cc-by-sa-40 diff --git a/lessons/beginners/fstring/index.md b/lessons/beginners/fstring/index.md deleted file mode 100644 index 0cef666d..00000000 --- a/lessons/beginners/fstring/index.md +++ /dev/null @@ -1,123 +0,0 @@ -# Šablony (formátovací řetězce) - -Řekněme, že chceš uživateli vypsat určitou hodnotu s nějakou „omáčkou“ okolo. -Dá se na to použít `print()`, kterému můžeš předat „mix“ řetězců a čísel: - -```pycon ->>> soucet = 3 + 4 ->>> print('Součet je', soucet) -``` - -Co ale když chceš celý tento výpis uložit do proměnné – jako jeden řetězec? -Čárka tu fungovat nebude, ta odděluje argumenty ve volání funkce. -Je potřeba `soucet` převést na řetězec a ten pak připojit k „omáčce“: - - -```pycon ->>> hlaska = 'Součet je ' + str(soucet) -``` - -To ale není tak přehledné, jak by mohlo. -Lze to zpřehlednit použitím šablony. - -Takovou šablonu si představ jako formulář s vynechanými místy: - -```plain -Součet je __________. -``` - -Složitější šablona by byla třeba tahle: - -```plain -Mil[ý/á] _______, -Váš výsledek je __________. - -S pozdravem, -_________ -``` - -Aby Python věděl, do kterého vynechaného místa co doplnit, je potřeba -jednotlivá vynechaná místa ve formuláři nějak jednoznačně označit. -Použijme jména v „kudrnatých“ závorkách: - -```plain -Součet je {soucet}. -``` - -```plain -Mil{y_a} {osloveni}, -Váš výsledek je {soucet}. - -S pozdravem, -{podpis}. -``` - -Takovou šablonu můžeš použít jako *formátovací řetězec* -(angl. [*formatted string literal*](https://docs.python.org/3.6/reference/lexical_analysis.html#formatted-string-literals), -zkráceně *f-string*). -Jako jakýkoli jiný řetězec ji vlož do uvozovek. -A aby bylo jasné, že jde o šablonu, před první uvozovky přidej navíc značku `f`. - -```python -f"Součet je {soucet}." -``` - -Takový formátovací řetězec jde použít v Pythonu – jako jakýkoli jiný řetězec: - -```python -soucet = 3 + 4 -hlaska = f'Součet je {soucet}' -print(hlaska) -``` - -```python -y_a = 'á' -osloveni = 'Anežko' -soucet = 3 + 4 -podpis = 'Váš Program' - -print(f""" -Mil{y_a} {osloveni}, -Váš výsledek je {soucet}. - -S pozdravem, -{podpis} -""") -``` - -A nakonec – v šabloně můžeš použít nejen jména proměnných, ale jakékoli výrazy. - -```pycon ->>> hlaska = f'Součet je {3 + 4}' -``` - -Ale nepřežeň to! -Většinou je program přehlednější, když si každou vypisovanou hodnotu zvlášť -pojmenuješ – tedy uložíš do vhodně pojmenované proměnné. - - -## Metoda format - -Někdy se stane, že jednu šablonu potřebuješ použít vícekrát. -Pak formátovací řetězec použít nemůžeš, protože se do něj proměnné doplňují -automaticky a hned. -V takovém případě můžeš šablonu napsat do normálního řetězce (bez `f` na -začátku) a použít metodu `format`: - -```python -sablona = 'Ahoj {jmeno}! Tvoje číslo je {cislo}.' -print(sablona.format(cislo=7, jmeno='Hynku')) -print(sablona.format(cislo=42, jmeno='Viléme')) -print(sablona.format(cislo=3, jmeno='Jarmilo')) -``` - -Oproti formátovacím řetězcům umí `format` užitečnou zkratku: nepojmenované -argumenty dosadí postupně do nepojmenovaných míst v šabloně: - -```python -vypis = '{} krát {} je {}'.format(3, 4, 3 * 4) -print(vypis) -``` - -Výrazy jako `f'Součet je {3 + 4}'` ale `format` dosadit neumí. -Složitější dosazované hodnoty si proto vždycky pojmenuj. diff --git a/lessons/beginners/fstring/info.yml b/lessons/beginners/fstring/info.yml deleted file mode 100644 index 7fb6a234..00000000 --- a/lessons/beginners/fstring/info.yml +++ /dev/null @@ -1,6 +0,0 @@ -title: Šablony (formátovací řetězce) -style: md -attribution: -- Pro PyLadies Brno napsal Petr Viktorin, 2018-2019. -license: cc-by-sa-40 -license_code: cc0 diff --git a/lessons/beginners/functions/index.md b/lessons/beginners/functions/index.md deleted file mode 100644 index 896ab95c..00000000 --- a/lessons/beginners/functions/index.md +++ /dev/null @@ -1,310 +0,0 @@ -# Funkce - -Známe spoustu matematických operací, které se zapisují pomocí symbolů – třeba -plus a minus. -Python se snaží používat stejné symboly jako matematici: - -* 3 + 4 -* a - b - -S násobením a dělením už je to složitější. -Matematický zápis se na běžné klávesnici nedá napsat: - -* 3 · 4 -* ¾ - -V Pythonu si ale pořád vystačíme se symbolem, byť trochu jiným – `*`, `/`. - -Matematici ale píšou na papír, a tak si můžou dovolit vymýšlet stále -zajímavější klikyháky, které se pak na klávesnici píšou stále hůř: - -* x² -* xy -* sin θ -* Γ(x) -* ∫x -* |s| -* ⌊x⌋ -* ab -* ab - -Ne že by neexistovaly programovací jazyky, -na které je potřeba speciální klávesnice. -Třeba program v jazyce APL laik jednoduše ani nenapíše, ani nepřečte: - - -```plain -⍎’⎕’,∈Nρ⊂S←’←⎕←(3=T)∨M∧2=T←⊃+/(V⌽”⊂M),(V⊖”⊂M),(V,⌽V)⌽”(V,V←1¯1)⊖”⊂M’ -``` - -Expert v APL může být vysoce produktivní, ale Python se zaměřuje spíš na to, -aby se dal snadno naučit. -A tak používá symboly jen pro ty nejčastější operace. -Operátorů, které využívají symboly, je tak málo, že už jich zhruba půlku znáš! - -> [note] -> Pro zajímavost, tady jsou všechny – i ty co ještě neznáš: -> -> -> ->
-> == != -> < > -> <= >= -> := -> | ^ -> & -> << >> -> + - -> * @ / -> // % -> ~ -> ** -> [ ] ( ) -> { } -> . ->
- -Všechno ostatní vyjádříme slovně. - - -## Délka řetězce - -Jedna operace, na kterou v Pythonu není symbol, je zjištění délky řetězce. -Místo symbolu má název. -Jmenuje se `len` (z angl. *length*, délka), a používá se takto: - -```python -slovo = 'Ahoj' -delka = len(slovo) # Vypočítání délky -print(delka) -``` - -To `len` je *funkce* (angl. *function*). -Jak se takové funkce používají? - -K tomu, abys funkci mohl{{a}} použít, potřebuješ znát její -*jméno* – tady `len`. -Za jméno funkce patří závorky, -do nichž uzavřeš *argument* (neboli *vstup*) funkce. -To je informace, se kterou bude funkce -pracovat – třeba `len` ze svého argumentu vypočítá délku. - -Celému výrazu `len(slovo)` se říká *volání funkce* (angl. *function call*). -Jeho výsledek, takzvaná *návratová* hodnota -(angl. *return value*) se dá třeba přiřadit do proměnné. - -{{ figure(img=static('call-anatomy.svg'), alt="Diagram volání funkce") }} - -> [note] Pro matemati{{gnd('', 'č', both='')}}ky -> Máš-li rád{{a}} matematiku, dej pozor! -> Funkce v Pythonu je něco jiného než funkce v matematice, -> i když se stejně jmenují a podobně zapisují. -> Pythonní funkce může např. mít pro stejný argument různé hodnoty. - - -### Volání funkce jako výraz - -Vzpomínáš si, jak Python vyhodnocuje výrazy? - -```python -vysledek = 3 * (5 + 2) -# ╰──┬──╯ -vysledek = 3 * 7 -# ╰─┬────╯ -vysledek = 21 -``` - -Volání funkce je taky výraz. -Stejně jako `a + b` je výraz, který něco udělá podle hodnot `a` a `b` -a výsledek dá k dispozici, `len(slovo)` je výraz, který něco udělá -podle hodnoty `slovo` a výsledek dá k dispozici. - -Vždycky, když Python při vyhodnocování narazí na jméno funkce se závorkami, -funkci *zavolá*, zjistí výsledek a dosadí ho: - -```python -vysledek = len("Ahoj!") -# ╰────┬─────╯ -vysledek = 5 -``` - -Volání funkce můžeš kombinovat s jinými výrazy, třeba se součtem: - -```python -delka = len('Ahoj') + len('!') -# ╰──┬─────╯ ╰─┬───╯ -delka = 4 + 1 -# ╰───────┬────╯ -delka = 5 -``` - -Nebo v podmínce ifu – třeba u: - -```python -if len('Ahoj!') <= 3: - print('pozdrav je krátký') -``` - -… se za `len('Ahoj!') <= 3` nakonec dosadí nepravda (`False`): - -```python - len('Ahoj!') <= 3 -# ╰─────┬────╯ - 5 <= 3 -# ╰──────┬──╯ - False -``` - -Volání funkce můžeš použít i jako argument pro jinou funkci: - -```python -print(len('Ahoj')) -# ╰────┬────╯ -print( 4 ) # vypíše 4 -``` - -Nebo to zkombinovat dohromady: - -```python -x = 5 -print(len('Ahoj') + x) -# ╰────┬────╯ | -print( 4 + 5) -# ╰───┬────╯ -print( 9 ) -``` - -… a podobně. - - -### Procedury - -Možná sis všiml{{a}}, že jednu funkci už voláš déle: `print("Ahoj!")` -je taky volání funkce. -Stejně jako `len` dostává `print` v závorkách argument – hodnotu, se -kterou pracuje. -Liší se ale návratovou hodnotou. - -Funkce `print` sice něco *udělá* – vypíše text -na obrazovku – ale nevrátí žádný smysluplný výsledek, který by zbytek programu -mohl dál zpracovat. - -Funkcím, které nic nevrací (jen něco udělají), se občas říká *procedury*. -V Pythonu není hranice mezi „normální“ funkcí a procedurou příliš ostrá, -ale přesto se hodí tento koncept znát. -Pár příkladů: - -* Funkce, která vybere náhodné číslo, je „normální“. - Svůj výsledek vrátí; program s ním může dál pracovat. -* Funkce, která vykreslí na obrazovku kolečko, je *procedura*. - Žádnou zajímavou hodnotu programu nevrací. -* Funkce, která spočítá průměrný věk obyvatelstva podle informací ze sčítání - lidu, je „normální“. Svůj výsledek vrátí a program s ním může dál pracovat. -* Funkce, která přehraje písničku reproduktorem, je *procedura*. - Nic zajímavého programu nevrací. - -> [note] -> Na rozdíl od ostatních termínů, které se tu učíš, není -> „procedura“ v Pythonu zavedený pojem. -> Je vypůjčený z jazyka Pascal. -> Kdybys o něm diskutoval{{a}} s nějakým zkušeným programátorem, -> odkaž ho prosím na tyto materiály. - - -## Argumenty - -Argument je to, co funkci dáš k dispozici. Hodnota, se kterou funkce pracuje. -Chceš-li délku řetězce `Ahoj!`, použiješ funkci `len`, která umí vypočítat -délku *jakéhokoli* řetězce, a jako argument, v závorkách, jí dáš tu svoji -konkrétní hodnotu: `len('Ahoj!')`. - -Podobně funkce `print` umí vypsat jakoukoli hodnotu. -Tu, kterou má vypsat ve tvém konkrétním případě, jí předáš jako argument. - -Některým funkcím můžeš předat i více argumentů. -Třeba zrovna funkci `print`, která všechny své argumenty vypíše na řádek. -Jednotlivé argumenty se oddělují čárkami: - -```python -print(1, 2, 3) -``` - -```python -print("Jedna plus dva je", 1 + 2) -``` - -Některé funkce nepotřebují žádný argument. -Příkladem je zase `print`. -Je ale nutné použít závorky – i když jsou prázdné. -Hádej, co tohle volání udělá? - -```python -print() -``` - -{% filter solution %} -Funkce `print` zavolaná bez argumentů napíše prázdný řádek. -{% endfilter %} - - -### Pojmenované argumenty - -Některé funkce umí pracovat i s *pojmenovanými* argumenty. -Píšou se podobně jako přiřazení do proměnné, -s rovnítkem, ale uvnitř závorek. - -Třeba funkce `print` při výpisu odděluje jednotlivé argumenty mezerou, -ale pomocí argumentu `sep` se dá použít i něco jiného. - -```python -print(1, 2, 3, 4, sep=', ') # Místo mezery odděluj čárkou -``` - -Dá se změnit i to, co `print` udělá na konci výpisu. -Normálně přejde na nový řádek, ale argumentem `end` můžeš říct, co se má vypsat -*místo toho*. - -> [note] -> Tenhle příklad je potřeba napsat do souboru; v interaktivní konzoli -> nebude výstup vypadat tak, jak má. - -```python -print('1 + 2', end=' ') # Místo přechodu na nový řádek jen napiš mezeru -print('=', end=' ') -print(1 + 2, end='!') -print() -``` - - -### Funkce je potřeba volat - -Pozor na to, že když nenapíšeš závorky, funkce se nezavolá! -Výraz `len(s)` je *volání funkce*, ale `len` bez závorek označuje -*funkci samotnou*. - -Výsledek výrazu `len(s)` je číslo; výsledek výrazu `len` je samotná funkce `len`. - -Čísla můžeš sčítat, můžeš tedy napsat `len(s) + 1`. -Funkce ale sčítat nejde – `len + 1` nedává smysl. - -Často se ale stane, že závorky prostě zapomeneš. -Zkus si, co dělají následující příklady, a pozorně si přečti výsledky -a chybové hlášky, abys pak podobné chyby poznal{{a}}: - -```python -print(len('a')) # Volání funkce (a vypsání výsledku) -print(len) # Vypsání samotné funkce -print(len + 1) # Sečtení funkce a čísla -``` - -## Přehled funkcí - -A jaké funkce můžeš, kromě `len` a `print`, použít? -Přehled těch základních najdeš v [následující lekci](../basic-functions). diff --git a/lessons/beginners/functions/info.yml b/lessons/beginners/functions/info.yml deleted file mode 100644 index 0c90dfb5..00000000 --- a/lessons/beginners/functions/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Funkce -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. -license: cc-by-sa-40 diff --git a/lessons/beginners/hello-world/index.md b/lessons/beginners/hello-world/index.md deleted file mode 100644 index 3cfe8d62..00000000 --- a/lessons/beginners/hello-world/index.md +++ /dev/null @@ -1,106 +0,0 @@ -# První program - -```pycon ->>> 3 + 4 -7 -``` - -Psaní příkazů přímo v Pythonu, interaktivně, -má jednu velkou nevýhodu: -to, co napíšeš, se ztratí, jakmile zavřeš okno příkazové řádky. -Na jednoduché výpočty to nevadí, ale až budou tvoje programy složitější, -budeš je potřebovat nějak uložit. - -Otevři editor -(Ten bys měl{{a}} mít nainstalovaný, jestli ne, instrukce jsou v [předchozí -lekci]({{ lesson_url('beginners/install-editor') }}).) - -V něm vytvoř nový soubor, do kterého napiš následující text: - -```python -print("Ahoj světe!") -``` - -Pak soubor ulož jako `ahoj.py`: - -* V adresáři, kde máš soubory ke kurzům Pythonu, si založ adresář pojmenovaný - podle čísla lekce (např. `02`). - Měl by být vedle tvého virtuálního prostředí. -* Do něj pak soubor ulož pod jménem `ahoj.py`. - -Pokud máš v ukládacím okýnku možnost zvolit *kódování*, zvol `UTF-8`. -Můžeš–li zvolit typ souboru, zvol `.py` nebo „všechny soubory“. - -## Spuštění - -Otevři si příkazovou řádku. -Pomocí `cd` donaviguj do adresáře, kde máš soubory ke kurzům Pythonu. - -> [note] -> S příkazovou řádkou jsme se seznámil{{gnd('i', 'y', both='i')}} -> v [minulé lekci](../../beginners/cmdline/), která popisuje i změnu aktuálního -> adresáře pomocí příkazu `cd`. - -Aktivuj si virtuální prostředí. - -> [note] -> Příkaz k tomu jsme si ukázali na konci -> [návodu na tvorbu virtuálního prostředí](../venv-setup/); končí `activate`. - - -Pak zadej tento příkaz: - -```console -(venv)$ python ahoj.py -``` - -Pokud se vypíše hláška `Ahoj světe!`, gratuluji! -Napsal{{a}} jsi svůj první program v Pythonu! - -Jestli to nefunguje, zkontroluj, že: - -* Máš zapnuté virtuální prostředí. - (Na příkazové řádce se musí ukazovat (venv); - pokud tam není, použij příkaz „activate“ z [minula]({{ lesson_url('beginners/install') }}).) -* Jsi ve správném adresáři. Zkus `pwd` (Unix) nebo `cd` (Windows). - Aktuální adresář musí být ten, do kterého jsi uložil{{a}} - soubor s programem. -* Soubor se opravdu jmenuje `ahoj.py`. - Pomocí `ls` (Unix) nebo `dir` (Windows) zkontroluj, že se soubor opravdu - jmenuje `ahoj.py` a ne třeba `ahoj.py.txt`. - Jestli ne, ulož ho znovu pod správným jménem. -* Soubor `ahoj.py` obsahuje správný příkaz, včetně všech uvozovek a závorek. -* Slovo `(venv)` ani znak `$` nezadáváš – v materiálech jsou proto, aby bylo - poznat, že jde o příkaz příkazové řádky. - Na `$` (nebo, na Windows, `>`) končí dotaz, který vypíše sám počítač. - Příkaz, který zadáváš ty, je jen `python ahoj.py`. - -A jestli to pořád nefunguje, zeptej se -{% if var('coach-present') -%} -kouče. -{%- else -%} -zkušenějšího programátora. -{% endif %} - - -> [style-note] Typografická vsuvka -> -> V Pythonu je většinou jedno, kde napíšeš mezeru. Stejně jako náš příkaz -> `print("Ahoj světe!")` by fungovalo třeba: -> -> ```python -> print ( "Ahoj světe!" ) -> ``` -> -> Je ale zvykem dodržovat určitá pravidla. -> Jako v češtině se po otvírací závorce a za -> ozavírací závorkou nepíše mezera. -> Na rozdíl od češtiny ale mezeru nepiš ani mezi `print` a závorkou. -> „Správně“ je tedy: -> -> ```python -> print("Ahoj světe!") -> ``` -> -> V rámci uvozovek má pak každá mezera význam: když napíšeš -> `" Ahoj světe!"`, mezery navíc se objeví ve výsledné hlášce. diff --git a/lessons/beginners/hello-world/info.yml b/lessons/beginners/hello-world/info.yml deleted file mode 100644 index 542a8828..00000000 --- a/lessons/beginners/hello-world/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: První program -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/inheritance/index.md b/lessons/beginners/inheritance/index.md deleted file mode 100644 index 24734965..00000000 --- a/lessons/beginners/inheritance/index.md +++ /dev/null @@ -1,272 +0,0 @@ -# Dědičnost - -Minule jsme probral{{gnd('i', 'y', both='i')}} třídy. -Jako příklad jsme si ukázal{{gnd('i', 'y', both='i')}} třídu pro koťátka: - -```python -class Kotatko: - def __init__(self, jmeno): - self.jmeno = jmeno - - def snez(self, jidlo): - print(f"{self.jmeno}: {jidlo} mi chutná!") - - def zamnoukej(self): - print(f"{self.jmeno}: Mňau!") -``` - -Zkus si udělat podobnou třídu pro štěňátka: - -```python -class Stenatko: - def __init__(self, jmeno): - self.jmeno = jmeno - - def snez(self, jidlo): - print(f"{self.jmeno}: {jidlo} mi chutná!") - - def zastekej(self): - print(f"{self.jmeno}: Haf!") -``` - -Většina kódu je stejná! -Kdybys měl{{a}} napsat i třídu pro kuřátka, kůzlátka, -slůňátka a háďátka, bez Ctrl+C by to bylo docela nudné. -A protože jsou programátoři líní psát stejný kód -několikrát (a hlavně ho potom udržovat), vymysleli -mechanismus, jak se toho vyvarovat. - -Koťátka i štěňátka jsou zvířátka. -Můžeš si vytvořit třídu společnou pro všechna -zvířátka a do ní napsat všechno, co je společné. -Ve třídách pro jednotlivé druhy zvířat pak zbude jen to, co se liší. -V Pythonu se to dělá takto: - -```python -class Zviratko: - def __init__(self, jmeno): - self.jmeno = jmeno - - def snez(self, jidlo): - print(f"{self.jmeno}: {jidlo} mi chutná!") - - -class Kotatko(Zviratko): - def zamnoukej(self): - print(f"{self.jmeno}: Mňau!") - - -class Stenatko(Zviratko): - def zastekej(self): - print(f"{self.jmeno}: Haf!") - - -micka = Kotatko('Micka') -azorek = Stenatko('Azorek') -micka.zamnoukej() -azorek.zastekej() -micka.snez('myš') -azorek.snez('kost') -``` - -Jak to funguje? -Příkazem `class Kotatko(Zviratko):` -říkáš Pythonu, že třída `Kotatko` -*dědí* ze třídy `Zviratko` -(angl. *inherits* from `Zviratko`). -Případně se můžeš setkat s jinými termíny: -„je odvozená” ze třídy `Zviratko`, -(angl. *derived from*), -nebo ji “rozšiřuje” (angl. *extends*). -A když už jsme u terminologie, odvozeným třídám se -říká taky *podtřídy* (angl. *subclasses*) -a `Zviratko` je tu *nadtřída* -(angl. *superclass*). - -Když potom Python hledá nějakou metodu -(nebo jiný atribut), třeba `micka.snez`, -a nenajde ji přímo ve třídě daného objektu (u nás -`Kotatko`), podívá se do nadtřídy. -Takže všechno, co je definované pro -`Zviratko`, platí i pro koťátka – dokud to výslovně nezměníš. - - -## Přepisování metod a `super()` - -Když se ti nebude líbit chování některé metody -v nadtřídě, stačí dát metodu stejného jména do -podtřídy: - -```python -class Kotatko(Zviratko): - def snez(self, jidlo): - print(f"{self.jmeno}: {jidlo} mi vůbec nechutná!") - - -micka = Kotatko('Micka') -micka.snez('granule') -``` - -> [note] -> Je to podobné jako když jsme minule přepisoval{{gnd('i', 'y', both='i')}} -> atribut pomocí `micka.zamnoukej = 12345`. -> Python atributy hledá napřed na samotném objektu, -> potom na třídě toho objektu a pak na nadtřídě -> (a případně dalších nadtřídách té nadtřídy). - -Občas se může stát, že v takovéto přepsané metodě budeš -potřebovat použít původní funkčnost, jen budeš chtít udělat ještě něco navíc. -To umí zařídit speciální funkce `super()`, -která umožňuje volat metody z nadtřídy. -Třeba takhle: - -```python -class Kotatko(Zviratko): - def snez(self, jidlo): - print(f"({self.jmeno} na {jidlo} chvíli fascinovaně kouká)") - super().snez(jidlo) - - -micka = Kotatko('Micka') -micka.snez('granule') -``` - -Pozor na to, že takhle volané metodě musíš dát všechny -argumenty, které potřebuje (kromě `self`, -který se jako obvykle doplní automaticky). -Toho se dá i využít – můžeš použít i jiné argumenty -než dostala původní funkce: - -```python -class Hadatko(Zviratko): - def __init__(self, jmeno): - jmeno = jmeno.replace('s', 'sss') - jmeno = jmeno.replace('S', 'Sss') - super().__init__(jmeno) - - -standa = Hadatko('Stanislav') -standa.snez('myš') -``` - -Jak je vidět, `super()` se dá bez problémů -kombinovat se speciálními metodami jako `__init__`. -Dokonce se to dělá poměrně často! - - -## Polymorfismus - -Programátoři nezavedli dědičnost jen proto, že jsou -líní a nechtějí psát dvakrát stejný kód. -To je sice dobrý důvod, ale nadtřídy mají jednu -důležitější vlastnost. Když víš, že každé -`Kotatko` nebo `Stenatko` -nebo jakákoli jiná podtřída je zvířátko, -můžeš si udělat seznam zvířátek s tím, -že pak bude jedno, jaká přesně zvířátka to jsou: - -```python -{# XXX: perhaps put these definitions back, but highlight the 4 lines below? -class Zviratko: - def __init__(self, jmeno): - self.jmeno = jmeno - - def snez(self, jidlo): - print(f"{self.jmeno}: {jidlo} mi chutná!") - - -class Kotatko(Zviratko): - def zamnoukej(self): - print(f"{self.jmeno}: Mňau!") - - -class Stenatko(Zviratko): - def zastekej(self): - print(f"{self.jmeno}: Haf!") -#} -zviratka = [Kotatko('Micka'), Stenatko('Azorek')] - -for zviratko in zviratka: - zviratko.snez('flákota') -``` - -Tohle je docela důležitá vlastnost podtříd: -když máš nějaké `Kotatko`, můžeš ho použít -kdekoliv kde program očekává `Zviratko`, -protože každé koťátko *je* zvířátko. - -> [note] -> Tohle je docela dobrá pomůcka pro případy, kdy nebudeš vědět, -> kterou třídu podědit z které. -> Každé *koťátko* nebo *štěňátko* -> je *zvířátko*, každá *chata* -> nebo *panelák* je *stavení*. -> V takových případech dává dědičnost smysl. -> -> Někdy se ale stane, že tuhle pomůcku zkusíš použít a vyjde ti -> nesmysl jako „každé auto je volant”. -> V takovém případě dědičnost nepoužívej. -> I když jak auto tak volant se dají „otočit doprava”, -> u každého to znamená něco jiného – a určitě nejde auto -> použít kdekoli, kde bys chtěl{{a}} použít volant. -> Takže v tomto případě je lepší si říct „každé auto -> *má* volant”, stejně jako „každé kotě -> *má* jméno”, udělat dvě nezávislé třídy a napsat něco jako: -> -> ```python -> class Auto: -> def __init__(self): -> self.volant = Volant() -> ``` -> -> (A až bude někdy nějaký vystudovaný informatik nespokojený -> s tím, že porušuješ -> [Liskovové substituční princip](https://en.wikipedia.org/wiki/Liskov_substitution_principle), -> jde o právě tento problém.) - -## Generalizace - -Když se teď podíváš na funkce `zamnoukej` -a `zastekej`, možná tě napadne, že by se -daly pojmenovat lépe, aby se daly použít pro všechna -zvířata, podobně jako `snez`. -Bude nejlepší je přejmenovat: - - -{# XXX: Every instance of "udelej_zvuk" should be highlighted #} -```python -class Zviratko: - def __init__(self, jmeno): - self.jmeno = jmeno - - def snez(self, jidlo): - print(f"{self.jmeno}: {jidlo} mi chutná!") - - -class Kotatko(Zviratko): - def udelej_zvuk(self): - print(f"{self.jmeno}: Mňau!") - - -class Stenatko(Zviratko): - def udelej_zvuk(self): - print(f"{self.jmeno}: Haf!") - - -zviratka = [Kotatko('Micka'), Stenatko('Azorek')] - -for zviratko in zviratka: - zviratko.udelej_zvuk() - zviratko.snez('flákota') -``` - -Jak tenhle příklad naznačuje, psát nadtřídy, ze kterých se dobře dědí, -není jednoduché. Zvlášť to platí, kdyby se z nich mělo dědit v jiném -programu, než kde je nadtřída. -I z toho důvodu je dobré dědičnost používat hlavně v rámci svého kódu: -nedoporučuji dědit od tříd, které napsali ostatní (jako `bool` nebo -`pyglet.sprite.Sprite`), pokud autor nadtřídy výslovně nezmíní, že (a jak) se -z ní dědit má. - -A to je zatím o třídách vše. Už toho víš dost na to, -aby sis napsal{{a}} vlastní zoo :) diff --git a/lessons/beginners/inheritance/info.yml b/lessons/beginners/inheritance/info.yml deleted file mode 100644 index ecfcc6f2..00000000 --- a/lessons/beginners/inheritance/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Dědičnost -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2015-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/install-editor/_base.md b/lessons/beginners/install-editor/_base.md deleted file mode 100644 index 4ed470fe..00000000 --- a/lessons/beginners/install-editor/_base.md +++ /dev/null @@ -1,79 +0,0 @@ -# Instalace {% block name_gen %} editoru {{ var('editor_name') }} {% endblock %} - - -{% block install %} - -Editor {{ editor_name }} -si stáhni z jeho [domovské stránky]({{ editor_url }}) -a nainstaluj. - -{% endblock %} - -## Nastavení - -{% block setup %} - -(Tohle by nemělo být vidět) - -{% endblock %} - - -## Nácvik odsazování - -Jak už bylo zmíňeno, v Pythonu je důležité, kolika mezerami řádek začíná. -Proto se nám bude hodit vědět, jak rychle odsazovat bloky textu. -Pojďme si ukázat, jak na to. - -Zkopíruj si do editoru tento text: - -``` -Ofelie: -Ach princi! -Jak má se Vaše Výsost už tak dlouho? -Hamlet: -Děkují poníženě: skvěle, skvěle, skvěle. -Ofelie: -Mám od vás, princi, stále ještě dárky, -Jež dávno toužím vrátit. Prosím vás, -račte je přijmout teď. -Hamlet: -Kdo? Já? Já nikdy -vám nedal nic. -Ofelie: -Dal, Výsosti. A spolu s dárky slova -tak rozmilá, že každý z nich -měl jejich vůni. Ta teď vyvanula, -a tak je vracím. Dary nejbohatší -se mění v trety, když se dárce mračí. -Zde, Výsosti. -``` - -(úryvek ze hry Hamlet, napsal W. Shakespeare, překlad E. A. Saudek) - - -Tenhle text není moc přehledný, tak ho zkusíme poodsazovat, aby vypadal takhle: - -``` -Ofelie: - Ach princi! - Jak má se Vaše Výsost už tak dlouho? -Hamlet: - Děkují poníženě: skvěle, skvěle, skvěle. -Ofelie: - Mám od vás, princi, stále ještě dárky, - Jež dávno toužím vrátit. Prosím vás, - račte je přijmout teď. -atd. -``` - -Abys odsadil{{a}} jeden řádek, nastav kurzor na začátek řádku a stiskni -klávesu Tab. -Každým stisknutím řádek odsadíš o 4 mezery. - -Odsadíš-li moc, pomocí Shift+Tab odsazení zmenšíš. - -Chceš-li odsadit víc řádků najednou, všechny je vyber a stiskni Tab. -I výběr můžeš „od-odsadit“ pomocí Shift+Tab. - - -A to je vše! Teď máš nejen nastavený editor, ale umíš ho i používat. diff --git a/lessons/beginners/install-editor/_linux_base.md b/lessons/beginners/install-editor/_linux_base.md deleted file mode 100644 index e5ed6874..00000000 --- a/lessons/beginners/install-editor/_linux_base.md +++ /dev/null @@ -1,22 +0,0 @@ -{% extends lesson.slug + '/_base.md' %} - -{% block install %} - -Na Linuxu se {{ editor_name }} instaluje jako ostatní programy: - -Fedora -: ```console - $ sudo dnf install {{ editor_cmd }} - ``` - -Ubuntu -: ```console - $ sudo apt-get install {{ editor_cmd }} - ``` - -Používáš-li jiný Linux, předpokládám že programy instalovat umíš. :) - -Pro Windows a macOS se {{ editor_name }} dá stáhnout z [domovské stránky]({{ editor_url }}). - -{% endblock %} - diff --git a/lessons/beginners/install-editor/atom.md b/lessons/beginners/install-editor/atom.md deleted file mode 100644 index 14eb4dc3..00000000 --- a/lessons/beginners/install-editor/atom.md +++ /dev/null @@ -1,48 +0,0 @@ -{% set editor_name = 'Atom' %} -{% set editor_url = 'https://atom.io' %} -{% extends lesson.slug + '/_base.md' %} - -{% block name_gen %} Atomu {% endblock %} - -{% block setup %} - -V Atomu se nemusí nic nastavovat, funguje „od výroby“ tak, jak má. - -Odsazování a obarvování bude fungovat správně jen v souborech s koncovkou `.py` -(jako Python). -V jiných programovacích jazycích se totiž odsazuje i obarvuje jinak. - -Proto jakmile v tomhle editoru vytvoříš nový soubor, -měl{{a}} bys ho co nejdřív uložit pod správným jménem. - -## Kontrola stylu zdrojového kódu - -Jedna věc nám v Atomu přeci jen chybí: plugin pro kontrolu správného -stylu zdrojového kódu. - -Tak jako čeština má Python typografická pravidla. -Například za čárkou se píše mezera, ale před ní ne. -Jsou nepovinná, program bude fungovat i při jejich nedodržení, -ale pomáhají psát přehledný kód, tak je dobré je dodržovat už od začátku. -Pravidla pro Python jsou popsána v dokumentu -[PEP8](https://www.python.org/dev/peps/pep-0008/). - -Aby sis je nemusel{{a}} všechny pamatovat, nainstaluj si plugin, -který tě na jejich porušení upozorní. - -Nejprve je potřeba si nainstalovat speciální knihovnu, která se o kontrolu -dokáže postarat. Do příkazové řádky zadej následující: - -```console -$ python -m pip install flake8 -``` - -A nyní si nainstaluj plugin do samotného editoru. V hlavní nabídce vyber -„Soubor > Nastavení/File > Settings“ a v nabídce -uprostřed okna vyber poslední položku -„Instalovat/Install“. Do vyhledávacího pole zadej -„linter-flake8“ a v seznamu nalezených pluginů klikni u položky stejného jména -na tlačítko „Instalovat/Install“. Bude ještě potřeba -schválit instalaci všech závislostí, na které se Atom postupně zeptá. - -{% endblock %} diff --git a/lessons/beginners/install-editor/gedit.md b/lessons/beginners/install-editor/gedit.md deleted file mode 100644 index 44661c58..00000000 --- a/lessons/beginners/install-editor/gedit.md +++ /dev/null @@ -1,37 +0,0 @@ -{% set editor_name = 'Gedit' %} -{% set editor_cmd = 'gedit' %} -{% set editor_url = 'https://wiki.gnome.org/Apps/Gedit' %} -{% extends lesson.slug + '/_linux_base.md' %} - -{% block name_gen %} Geditu {% endblock %} - - -{% block setup %} - -Gedit se nastavuje v Předvolbách (Preferences). - -{{ figure(img=static('gedit_prefs.png'), alt="") }} - -Číslování řádků -: V sekci Zobrazit/View vyber - Zobrazovat čísla řádků/Display Line Numbers. - - {{ figure(img=static('gedit_linenums.png'), alt="") }} - -Odsazování -: V sekci Editor vyber: - - * Šířka tabulátorů/Tab width: 4 - * Vkládat mezery místo tabulátorů/Insert spaces instead of tabs - * Povolit automatické odsazování/Enable automatic indentation - - {{ figure(img=static('gedit_indent.png'), alt="") }} - -Obarvování -: Obarvování funguje automaticky, ale způsob obarvování se vybírá podle - koncovky souboru – např. `.py` pro Python. - - Proto jakmile v tomhle editoru vytvoříš nový soubor, měl{{a}} bys ho co - nejdřív uložit pod správným jménem. - -{% endblock %} diff --git a/lessons/beginners/install-editor/index.md b/lessons/beginners/install-editor/index.md deleted file mode 100644 index 6fdc3731..00000000 --- a/lessons/beginners/install-editor/index.md +++ /dev/null @@ -1,123 +0,0 @@ -# Instalace editoru - -Editor, program na úpravu textu, je základní pomůcka -každého programátora, -takže je dobré do něj investovat trochu času. - -Je víceméně jedno, který programátorský editor budeš používat. -Pokud už nějaký oblíbený máš, stačí ho jen nastavit; -jestli ne, nějaký ti doporučíme. -Pokud ale používáš Poznámkový blok (Notepad) z Windows, -nebo TextEdit (editor předinstalovaný v macOS), -nebude ti stačit. -Stejně tak nejsou vhodné programy jako Word či Writer. - - -## Co programátorský editor umí - -Editor pro programátory nám umožňuje upravovat *prostý text* – písmenka. -Na rozdíl od programů jako Word, Writer či Pages neumožňuje text *formátovat*, -tedy dělat nadpisy, obarvovat, zvětšovat font, vkládat obrázky a podobně. - -Pomocí editoru budeme zadávat počítači příkazy, takže formátování nepotřebujeme. -Porovnej {{ gnd('sám', 'sama') }}, jaký je rozdíl mezi následujícími příkazy -pro někoho, kdo se jimi má řídit: - -* Nakresli mi beránka! -* Nakresli mi beránka! - -I když neumí formátování, neznamená to, že jsou naše editory úplně „hloupé“ -nástroje. -Aby se nám programy upravovaly pohodlněji, mají několik vychytávek: - -Podpora více souborů -: Větší projekty sestávají z více souborů, které můžeš mít v editoru - otevřené všechny najednou. - -Číslování řádků -: Před každým řádkem se ukazuje číslo. - To se bude velice hodit, až Python bude nadávat, že chyba je na řádku 183. - -Odsazování -: V Pythonu je důležité, kolika mezerami řádek začíná. - Správně nastavený editor nám odsazování značně zjednoduší. - -Obarvování -: Ačkoli nemůžeme u jednotlivých písmenek nastavovat barvu přímo, editor nám - obarvením může napovědět, jak našim instrukcím bude počítač rozumět. - Ale je to jenom nápověda: - programátor s jinak nastaveným editorem může mít stejný soubor obarvený - docela jinak. - -> [note] -> -> Pro ilustraci, takhle může v editoru vypadat kousek kódu: -> -> ```python -> 1 @app.route('/courses//') -> 2 def course_page(course): -> 3 try: -> 4 return render_template( -> 5 'course.html', -> 6 course=course, -> 7 plan=course.sessions, -> 8 ) -> 9 except TemplateNotFound: -> 10 abort(404) -> ``` - - -## Volba a nastavení editoru - -Vybereš-li editor, klikni na jeho jméno a dostaneš se na instrukce ke stažení -a nastavení. -(Na tuhle stránku se pak už nemusíš vracet.) - -* [Visual Studio Code]({{ subpage_url('vscode') }}) – doporučený editor pro - Windows a macOS (a vhodný i pro Linux). - V poslední době je to asi nejpopulárnější editor kódu. - Nabízí mnoho funkcí a má velkou základnu uživatelů a vývojářů, - takže se neustále vylepšuje. - -Na Linuxu budeš mít pravděpodobně už nainstalovaný Gedit nebo Kate. -Zkus se podívat do systémové nabídky, jestli jeden z nich máš (případně je -spusť z příkazové řádky jako `gedit`, resp. `kate`). -Pokud ano, klikni na odkaz níže a editor si nastav. -Nemáš-li ani jeden, vyber Visual Studio Code (viz výše). - -* [Gedit]({{ subpage_url('gedit') }}) – bývá na systémech s prostředím GNOME. -* [Kate]({{ subpage_url('kate') }}) – bývá na systémech s prostředím KDE. - -Existují i jiné editory, na které máme návody -nebo jsme je doporučovali ve starších verzích těchto materiálů. -Pokud se jeden z nich rozhodneš použít, neuděláš chybu: - -* [Atom]({{ subpage_url('atom') }}) – kvalitní editor s jednoduchým designem -* [Notepad++]({{ subpage_url('notepad-plus-plus') }}) – nenáročný editor - pro Windows vhodný pro pomalejší počítače - -Máš-li už svůj oblíbený editor – Vim, Emacs, Geany, apod., použij ten: - -* [Ostatní]({{ subpage_url('others') }}) – máš-li jiný editor, zkontroluj - si, že je správně nastaven. - -### IDE - -Existují i složitější a mocnější editory, takzvané *IDE* (angl. *Integrated -Development Environment*, integrované vývojové prostředí), -třeba [PyCharm], [Eclipse] nebo [KDevelop]. -Umí spoustu pokročilých funkcí, které programátorům pomáhají: -našeptávání, přejmenovávání, spouštění programů, správu virtuálních prostředí -a podobně. -Na začátek ale nejsou moc vhodné. - -Chceš-li takový editor přesto použít, měl{{a}} bys ho už poměrně dobře znát: -vědět, co za tebe dělá editor a jak to spravit, až něco udělá špatně. -{% if var('coach-present') -%} -Koučové většinou znají jen jeden editor – ten, který používají – -takže nemusí být schopní s pokročilým IDE rychle pomoct. -{%- endif %} - -[PyCharm]: https://www.jetbrains.com/pycharm/ -[Eclipse]: https://eclipse.org/ -[KDevelop]: https://www.kdevelop.org/ diff --git a/lessons/beginners/install-editor/info.yml b/lessons/beginners/install-editor/info.yml deleted file mode 100644 index 60ed632d..00000000 --- a/lessons/beginners/install-editor/info.yml +++ /dev/null @@ -1,23 +0,0 @@ -title: Instalace editoru -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Tomáš Roj (2019) -license: cc-by-sa-40 -subpages: - vscode: - title: Instalace Visual Studio Code - subtitle: Visual Studio Code - atom: - title: Instalace Atomu - subtitle: Atom - kate: - title: Instalace Kate - subtitle: Kate - gedit: - title: Instalace Geditu - subtitle: Gedit - notepad-plus-plus: - title: Instalace Notepadu++ - subtitle: Notepad++ - others: - title: Nastavení editoru - subtitle: Nastavení diff --git a/lessons/beginners/install-editor/kate.md b/lessons/beginners/install-editor/kate.md deleted file mode 100644 index 8bf2ffba..00000000 --- a/lessons/beginners/install-editor/kate.md +++ /dev/null @@ -1,47 +0,0 @@ -{% set editor_name = 'Kate' %} -{% set editor_cmd = 'kate' %} -{% set editor_url = 'https://kate-editor.org/get-it/' %} -{% extends lesson.slug + '/_linux_base.md' %} - -{% block name_gen %} Kate {% endblock %} - - -{% block setup %} - -Číslování řádků -: V menu Pohled/View vyber - Ukazovat čísla řádek/Show Line Numbers. - -Odsazování -: V Menu Nastavení/Settings vyber - Nastavit 'Kate'/Configure Kate. - - Tam v Úpravy/Editing vyber - Odsazování/Indentation. - - Tam nastav: - - * Výchozí režim odsazení/Default indentation mode: Python - * Odsazovat pomocí/Indent using: Mezer/Spaces - * Šířka tabulátoru/Tab Width: 4 znaky - * Odsadit pomocí/Indentation width: 4 znaky - * Klávesa Backspace zpětně odsazuje v úvodních mezerách/Backspace key in leading blank space unindents - -Obarvování -: Obarvování funguje automaticky, ale způsob obarvování se vybírá podle - koncovky souboru – např. `.py` pro Python. - - Proto, jakmile v tomhle editoru vytvoříš nový soubor, měl{{a}} bys ho co - nejdřív uložit pod správným jménem. - -Psaní < ve Windows -: Pokud používáš českou klávesnici, je možné, že ti ve výchozím nastavení - nepůjde v Kate napsat znak `<`. Při stisknutí kláves - Ctrl+Alt+, nebo - AltGr+, se místo napsání `<` zobrazí okno Nastavit - klávesové zkratky/Configure keybord shortcuts. V - tom případě v tomto okně najdi klávesovou zkratku pro Nastavit klávesové - zkratky/Configure keybord shortcuts a změn ji na - Žádná/None. - -{% endblock %} diff --git a/lessons/beginners/install-editor/notepad-plus-plus.md b/lessons/beginners/install-editor/notepad-plus-plus.md deleted file mode 100644 index 1d44fcdc..00000000 --- a/lessons/beginners/install-editor/notepad-plus-plus.md +++ /dev/null @@ -1,31 +0,0 @@ -{% set editor_name = 'Notepad++' %} -{% set editor_url = 'https://notepad-plus-plus.org/' %} -{% extends lesson.slug + '/_base.md' %} - -{% block name_gen %} Notepadu++ {% endblock %} - -{% block install %} - -Notepad++ je k dispozici pouze pro Windows. - -Stáhni jej z jeho [domovské stránky](https://notepad-plus-plus.org/) -a nainstaluj. - -{% endblock %} - - -{% block setup %} - -Odsazování -: V menu Nastavení zvol Předvolby a pak nastav - „Nastavení tabulátoru/Tab Settings“ na - „Zaměnit za mezery/Replace by Space“. - -Obarvování bude fungovat automaticky v souborech s koncovkou `.py` -(jako Python). -V jiných programovacích jazycích se totiž odsazuje i obarvuje jinak. - -Proto, jakmile v tomhle editoru vytvoříš nový soubor, -měl{{a}} bys ho co nejdřív uložit pod správným jménem. - -{% endblock %} diff --git a/lessons/beginners/install-editor/others.md b/lessons/beginners/install-editor/others.md deleted file mode 100644 index eb000d7f..00000000 --- a/lessons/beginners/install-editor/others.md +++ /dev/null @@ -1,65 +0,0 @@ -{% extends lesson.slug + '/_linux_base.md' %} - -{% block name_gen %} editoru {% endblock %} - -{% block install %} - -Používáš-li editor, pro který nemáme instrukce, budeš ho muset nastavit -{{ gnd('sám', 'sama') }}. -Tady je pár tipů, na co si dát pozor. - -{% endblock %} - -{% block setup %} - -## Číslování řádků - -Ujisti se, že ti editor čísluje řádky. -Pokud ne, podívej se do nastavení a zjisti, jak se to zapíná. - - -## Obarvování - -Ulož soubor s koncovkou `.py` – například `zkouska.py` – a zkopíruj do něj -následující program: - -```python -def foo(): - return "abc" * 2 -``` - -Jestli se text automaticky obarví (klidně jinými barvami než tady), -je tvůj editor nastavený správně. -Jinak se podívej do nastavení a zjisti, jak se to zapíná. - - -## Odsazování - -Stisknutím klávesy Tab na *začatku řádku* se vloží 4 mezery. -Pro psaní a sdílení kódu v Pythonu je důležité, -aby byly čtyři a aby to byly opravdu mezery. - -Jestli to jsou mezery, se dá zjistit tak, že odsazení na začátku vybereš myší. -Jde-li vybírat po jednotlivých mezerách, je všechno v pořádku. - -Nejde-li vybírat po jednotlivých mezerách, nebo pokud se jich po stisknutí -Tab vloží jiný počet než 4, podívej se do nastavení po možnostech -jako „velikost odsazení“ nebo „nahrazovat tabulátory za mezery”. - - -## Kontrola stylu zdrojového kódu - -Editory často podporují instalaci pluginů, které mohou psaní kódu usnadnit -a pomoci s jeho kontrolou. -Jeden z neužitečnějších je plugin pro kontrolu správného stylu zdrojového kódu. - -Tak jako čeština má Python typografická providla. -Například za čárkou se píše mezera, ale před ní ne. -Jsou nepovinná, program bude fungovat i při jejich nedodržení, -ale pomáhají psát přehledný kód, tak je dobré je dodržovat už od začátku. -Tato pravidla jsou popsána -v dokumentu [PEP8](https://www.python.org/dev/peps/pep-0008/). - -Zkus takový plugin pro svůj editor najít a nainstalovat. - -{% endblock %} diff --git a/lessons/beginners/install-editor/vscode.md b/lessons/beginners/install-editor/vscode.md deleted file mode 100644 index 8f9fb894..00000000 --- a/lessons/beginners/install-editor/vscode.md +++ /dev/null @@ -1,31 +0,0 @@ -{% set editor_name = 'Visual Studio Code' %} {% set editor_url = 'https://code.visualstudio.com' %} -{% extends lesson.slug + '/_base.md' %} - -{% block name_gen %}Visual Studio Code{% endblock %} - -{% block install %} -## Stažení a instalace - -Editor si můžeš stáhnout z jeho [domovské stránky](https://code.visualstudio.com/). -Vyber na ní zelené tlačítko Download a vyber instalátor pro svůj systém. -Dále se řiď instrukcemi instalátoru jako u každého jiného programu. -{% endblock %} - -{% block setup %} -Ve Visual Studio Code se nemusí nic nastavovat, funguje „od výroby“ tak, jak má. - -### Odesílání telemetrických dat - -Tento textový editor ale odesílá data o tvém používání ([nejspíš včetně např. -obsahu otevřených souborů][privacy]). -Pokud si nepřeješ aby se data odesílala, můžeš odesílání zrušit: - -* Otevři **File** > **Preferences** > **Settings** (macOS: **Code** > **Preferences** > **Settings**). -* Vyhledej `telemetry.enableTelemetry` a odškrtni tento záznam. - -Viz též [původni postup v angličtině](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting). - -[privacy]: https://privacy.microsoft.com/en-us/privacystatement - - -{% endblock %} diff --git a/lessons/beginners/install/index.md b/lessons/beginners/install/index.md deleted file mode 100644 index 7d812a57..00000000 --- a/lessons/beginners/install/index.md +++ /dev/null @@ -1,18 +0,0 @@ -# Instalace Pythonu - -V této sekci nainstalujeme Python. - -To se na každém druhu operačního systému dělá trošku jinak. -Vyber si stránku podle svého systému: - -* [Linux]({{ subpage_url('linux') }}) -* [Windows]({{ subpage_url('windows') }}) -* [macOS]({{ subpage_url('macos') }}) - -Pokud máš jiný systém než Linux, Windows nebo macOS, -nebo pokud ke svému počítači neznáš administrátorské heslo, -{% if var('coach-present') -%} -poraď se s koučem hned, jinak se ptej, až bude něco nejasné. -{%- else -%} -napiš nám prosím e-mail. {# XXX vyřešit kam poslat samostudenty co mají problém #} -{%- endif %} diff --git a/lessons/beginners/install/info.yml b/lessons/beginners/install/info.yml deleted file mode 100644 index ea44bc47..00000000 --- a/lessons/beginners/install/info.yml +++ /dev/null @@ -1,11 +0,0 @@ -title: Instalace Pythonu -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Martin Pavlásek (2023) -license: cc-by-sa-40 -subpages: - linux: - subtitle: Linux - windows: - subtitle: Windows - macos: - subtitle: macOS diff --git a/lessons/beginners/install/linux.md b/lessons/beginners/install/linux.md deleted file mode 100644 index b589a8a2..00000000 --- a/lessons/beginners/install/linux.md +++ /dev/null @@ -1,113 +0,0 @@ -# Instalace Pythonu na Linux - -Nainstalovat Pyhon na Linux je většinou jednoduché. -Jen existuje spousta druhů Linuxu a máme s ním největší zkušenosti, -tak jsou tyhle instrukce trochu delší. -Nezalekni se – většinu sekcí pravděpodobně přeskočíš. :) - -## Instalace Pythonu 3 - -Na Linuxu většinou Python 3 už bývá. Abys to zkontroloval{{a}}, spusť -v [příkazové řádce]({{ lesson_url('beginners/cmdline') }}) příkaz: - -```console -$ python3 --version -``` - -Objeví-li se „Python“ a číslo verze (např. `Python 3.6.6`) -a verze je 3.6 nebo vyšší, máš nainstalováno. -Přejdi na další sekci, [kontrolu `tkinter`](#check-tkinter). - -Objeví-li se „Python“ a verze 3.5 nebo nižší, -{% if var('coach-present') -%} -poraď se s koučem. -{%- else -%} -aktualizuj systém (nebo se poraď s někým, kdo to umí) a zkus to znovu. -{%- endif %} - -Objeví-li se `bash: python3: command not found` nebo podobná chyba, -doinstaluj Python. -Konkrétní příkaz záleží na distribuci: - -* **Fedora**: - {% filter markdown(inline=True) %} - ```console - $ sudo dnf install python3 - ``` - {% endfilter %} -* **Ubuntu**: - {% filter markdown(inline=True) %} - ```console - $ sudo apt-get install python3 - ``` - {% endfilter %} - -Používáš-li jinou distribuci, doufám, že instalovat programy už umíš. - - -{{ anchor('check-tkinter') }} -## Kontrola Tkinter - -Některé linuxové distribuce obsahují standardně jen část celkové funkčnosti -Pythonu. -Konkrétně knihovnu `tkinter` (která umožňuje např. kreslit „želví obrázky“) -často musíme nainstalovat zvlášť. -Abys zjistil{{a}}, jestli je už je nainstalovaná, zadej příkaz: - -```console -$ python3 -m tkinter -``` - -Objeví-li se okýnko, je všechno v pořádku. -Zavři ho a přejdi na [doinstalování `virtualenv`](#install-virtualenv). - -Jestli ne, modul `tkinter` ještě nainstaluj: - -* **Fedora**: - {% filter markdown(inline=True) %} - ```console - $ sudo dnf install python3-tkinter - ``` - {% endfilter %} -* **Ubuntu**: - {% filter markdown %} - ```console - $ sudo apt-get install python3-tk - ``` - {% endfilter %} - -Používáš-li jinou distribuci, musíš si správné jméno balíčku najít na Internetu. - -{{ anchor('install-virtualenv') }} -## Doinstalování Virtualenv - -Novější verze Pythonu mají zabudovaný nástroj `venv`, který použijeme níže. -Starší verze ho ale nemají (a některé distribuce Linuxu ho dokonce z Pythonu -vyřadily). -Potřebuješ proto zjistit, jestli `venv` máš, a případně nainstalovat alternativu. - -Spusť v příkazové řádce příkaz: - -```console -$ python3 -m ensurepip --version -``` - -Objeví-li se výpis začínající „pip“, máš funkční `venv` nainstalovaný. -Zbytek této sekce můžeš přeskočit! - -Objeví-li se ale nápis `No module named ensurepip`, je potřeba doinstalovat -alternativu, Virtualenv: - - - -* **Ubuntu**: - {% filter markdown(inline=True) %} - ```console - $ sudo apt-get install python3-virtualenv - ``` - {% endfilter %} - -Používáš-li jinou distribuci, doufám, že instalovat programy už umíš. - -Instaluješ-li Virtualenv, **zapamatuj si**, že ho budeš muset použít později -při vytváření virtuálního prostředí. diff --git a/lessons/beginners/install/macos.md b/lessons/beginners/install/macos.md deleted file mode 100644 index a7a1074a..00000000 --- a/lessons/beginners/install/macos.md +++ /dev/null @@ -1,33 +0,0 @@ -# Instalace Pythonu pro macOS - -Nainstaluj si nástroj [Homebrew](http://brew.sh), který řeší a zjednodušuje -instalaci aplikací a knihoven, které budeme potřebovat pro programování. -Jak na to? - -Spusť v [příkazové řádce]({{ lesson_url('beginners/cmdline') }}) příkaz: - -```console -$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -``` - -Pak zadej následující příkaz a Python bude nainstalovaný: - -```console -$ brew install python3 -``` - -Zkontroluj si, že máš verzi 3.6 nebo vyšší: - -```console -$ python3 --version -``` - -Objeví-li se „Python“ a číslo verze (např. `Python 3.6.6`) -a verze je 3.6 nebo vyšší, máš nainstalováno. -Jinak je něco špatně; -{% if var('coach-present') -%} -poraď se s koučem. -{%- else -%} -zkus instalaci znovu. -Když to nevyjde, poraď se s někým zkušenějším. -{%- endif %} diff --git a/lessons/beginners/install/windows.md b/lessons/beginners/install/windows.md deleted file mode 100644 index 3e8fc45e..00000000 --- a/lessons/beginners/install/windows.md +++ /dev/null @@ -1,64 +0,0 @@ -# Instalace Pythonu pro Windows - -## Zjisti, jestli už máš Python nainstalovaný - -Otevři si příkazovou řádku (pokud nevíš o co jde, přečti si o ní [tady]({{ lesson_url('beginners/cmdline') }})). - -Napiš `python` a zmáčkni na klávesnici Enter. -Teď se může stát několik různých věcí: -1. Otevře se ti Microsoft Store → okno zavři a pokračuj v kroku [Stažení](#download) -2. Zobrazí se ti text podobný tomuto níže? -```plain -> python -Python 3.8.1 (...) -Type "help", "copyright", "credits" or "license" for more information. ->>> -``` -To je dobrá zpráva! Ještě si zkontroluj, jakou verzi máš nainstalovanou -(např.`Python 3.11.2` je `3.11`, `Python 3.8.1` je `3.8` atd.). -Třetí číslo za tečkou není tady podstatné. - - Je verze `3.6` nebo novější? Výborně, máš instalaci hotovou! - Okénko s příkazovou řádkou můžeš zavřít. - Až ho budeš znovu potřebovat, můžeš otevřít nové. - Pokračuj dále [Nastavením prostředí]({{ lesson_url('beginners/venv-setup') }}). -V opačném případě přejdi na [Stažení](#download) - - -{{ anchor('download') }} -## Stažení -- Běž na [stahovací stránku](https://www.python.org/downloads/windows/) -- klikni na *Latest Python 3 Release* -- v části *Files*, ve sloupci *Version*, vyber vhodný *Windows installer* pro tvou verzi Windows (32 nebo 64bit). - -> [note] -> Kde zjistíš, zda máš 32bitové nebo 64bitové Windows? Otevři nabídku -> **Start**, vyhledat „Systém“ a otevřít **Systémové informace**. -> Pokud máš novější počítač, téměř jistě budeš mít Windows 64bitové. -> -> {{ figure( - img=static('windows_32v64-bit.png'), - alt='Screenshot zjišťování verze systému', -) }} - -## Instalace - -Stažený instalátor spusť. - -Na začátku instalace zaškrtni: -- **Use admin privileges when installing py.exe** -- a také **Add python.exe to PATH**. -Tyto volby ti zjednoduší vytvoření virtuálního prostředí. - -(Jestli nemáš administrátorské oprávnění, volbu -*Use admin privileges when installing py.exe* nezaškrtávej.) - -{{ figure( - img=static('windows_add_python_to_path.png'), - alt='Screenshot instalace Pythonu', -) }} - -Pak zmáčkni **Install now** a dále se drž instrukcí. - -Máš-li otevřenou příkazovou řádku, po instalaci Pythonu ji zavři a otevři -novou. -Instalace mění systémové nastavení, které se musí načíst znovu. diff --git a/lessons/beginners/interfaces/index.md b/lessons/beginners/interfaces/index.md deleted file mode 100644 index f1bed500..00000000 --- a/lessons/beginners/interfaces/index.md +++ /dev/null @@ -1,107 +0,0 @@ -# Rozhraní - -Už víš, že funkce ti umožňují kousek kódu: - -* použít (zavolat) na více místech v programu, i když definice je jen jedna, -* vyčlenit, aby detail (jako načtení čísla od uživatele) „nezavazel“ ve větším - programu, který tak může být přehlednější, a -* pojmenovat, aby bylo jasné, co kód dělá, i bez toho, abys musel{{a}} číst - samotné tělo funkce. - -Další výhoda funkce je, že ji můžeš jednoduše vyměnit za jinou, -lepší funkci – pokud má ta lepší funkce stejné *rozhraní* (angl. *interface*). - -Aby se ti líp představovalo, o čem budeme povídat, představ si elektrickou -zásuvku ve zdi. -Do takové zásuvky můžeš zapojit počítač, lampu, nabíječku na mobil, vysavač, -nebo rádio. -Zásuvka poskytuje elektrický proud; je jedno, jak ho použiješ. -Stejně tak je jedno, jestli je „druhý konec“ zásuvky připojený k solárnímu -panelu nebo k atomové elektrárně. -Zásuvka poskytuje elektrický proud, a jsou u ní důležité určité parametry -(tvar, napětí, frekvence, maximální proud), na kterých se obě strany, -poskytovatel proudu i spotřebič, shodly. -Tyhle parametry tvoří *rozhraní*, které umožňuje připojit jakýkoli spotřebič -k jakékoli elektrárně. - - -## Rozhraní funkce - -Podívej se na tuhle hlavičku funkce. -Je z ní poznat, co ta funkce dělá a jak ji použít? - -```python -def ano_nebo_ne(otazka): - """Zeptá se uživatele na otázku a vrátí True nebo False dle odpovědi""" - ... -``` - -Podobnou funkci už jsi napsal{{a}}. -Když zavoláš `ano_nebo_ne('Chutná ti čokoláda?')`, otázka se objeví -na příkazové řádce. -Když uživatel odpoví, funkce vrátí `True` nebo `False`. - -Co kdybys ale měl{{a}} následující funkci? - -```python -def ano_nebo_ne(otazka): - """Ukáže tlačítka "Ano" a "Ne" a až uživatel jedno zmáčkne, vrátí True - nebo False dle stisknutého tlačítka.""" - ... -``` - -Screenshot s tlačítky Ano a Ne - -Když zavoláš tuhle funkci, `ano_nebo_ne('Chutná ti čokoláda?')`, ukáže se -okýnko se dvěma tlačítky. -Když uživatel jedno zmáčkne, funkce vrátí `True` nebo `False`. - -Z hlediska programu se nic nemění: jediné, co se změní, je *definice funkce*; -volání a práce s návratovou hodnotou je pak stejné jako dřív. - - -### Vyzkoušej si to! - -Najdi nějaký svůj program, který používá `ano_nebo_ne`, případně jen `print` -a `input`. - -Stáhni si modul tkui.py -do adresáře se svým programem. -Naimportuj z něho funkce, které potřebuješ. -Jsou k dispozici čtyři: - -```python -from tkui import input, nacti_cislo, ano_nebo_ne, print -``` - -Tento import *přepíše* vestavěné funkce `input` a `print` variantami, -které mají (téměř) stejné rozhraní – jen dělají něco trochu jinak. - -Případné vlastní definice funkcí `nacti_cislo` a `ano_nebo_ne` pak z programu -vyndej, aby se použily ty naimportované. - -> [note] -> Funkce `tkui.nacti_cislo` potřebuje Python verze 3.7 nebo vyšší. -> Používáš-li Python 3.6, `nacti_cislo` nenahrazuj. - -Program by měl fungovat stejně jako dřív! - -Je to tím, že tyto funkce mají stejné *rozhraní* jako jejich dřívější protějšky. -Rozhraní funkce tvoří všechno, co potřebuje kód, který funkce volá: - -* jméno, kterým se funkce volá, -* argumenty, které bere (např. `input` bere otázku jako řetězec; `print` - může brát více argumentů k vypsání), a -* návratová hodnota, se kterou program pracuje dál (např. `input` vrací - řetězec; `print` nevrací nic smysluplného). - -Nékteré z těchto informací musíš do hlavičky funkce napsat vždy. -Ty ostatní je dobré popsat v dokumentačním řetězci, aby ten, kdo chce funkci -použít, věděl jak na to. - - -> [note] -> Modul `tkui` je jen ilustrační. Je udělaný tak, aby se dobře “instaloval” -> spíš než aby ti pomohl psát reálné programy. -> V tomto kurzu se vrátíme zpět k příkazové řádce, která je dělaná tak, -> aby byla užitečná pro programátory. diff --git a/lessons/beginners/interfaces/info.yml b/lessons/beginners/interfaces/info.yml deleted file mode 100644 index c4c4128a..00000000 --- a/lessons/beginners/interfaces/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Rozhraní -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2020. -license: cc-by-sa-40 diff --git a/lessons/beginners/list/index.md b/lessons/beginners/list/index.md deleted file mode 100644 index 85ec1378..00000000 --- a/lessons/beginners/list/index.md +++ /dev/null @@ -1,536 +0,0 @@ -Encyklopedické informace z této stránky shrnuje -[Tahák na seznamy](https://github.com/pyvec/cheatsheets/blob/master/lists/lists-cs.pdf), -který si můžeš vytisknout. - -# Seznamy - -Dnes si ukážeme, jak pracovat se *seznamy* (angl. *lists*). - -Začneme prakticky. -Vytvoř si seznam pomocí následujícího kódu: - -```python -zviratka = ['pes', 'kočka', 'králík'] -print(zviratka) -``` - -> [note] -> Nemůžeš najít hranaté závorky? -> Na české klávesnici zkus pravý Alt + F a G. - -Seznam je hodnota, která může obsahovat spoustu dalších hodnot. -Tak jako řetězec obsahuje sekvenci znaků, -seznam obsahuje sekvenci jakýchkoli hodnot. -V našem případě obsahuje sekvenci řetězců. - -A tak jako můžeš pomocí cyklu `for` procházet řetězec po znacích, -seznam můžeš procházet po jednotlivých prvcích: - -```python -for zviratko in zviratka: - print(zviratko) -``` - -Seznamy se v programech vyskytují často: -soubor se dá načíst jako seznam jednotlivých řádků, -matematika je plná číselných řad, e-shopy pracují se seznamy zboží, -seznam řetězců jako `'7♥'` a `'K♣'` může posloužit jako balíček karet. - -Hodnoty v seznamu můžou být jakéhokoli typu: - -```python -prvni_prvocisla = [2, 3, 5, 7, 11] -``` - -Dokonce můžeš různé typy míchat v jednom seznamu. -(S takovými namixovanými seznamy se ovšem příliš často nesetkáš. -Různé typy hodnot se používají spíš v n-ticích, o kterých si povíme -později): - -```python -seznam = [1, 'abc', True, None, range(10), len] -print(seznam) -``` - -## Neměnitelné hodnoty - -Důležitá vlastnost seznamů je, že se dají *měnit*. - -Než vysvětlíme, o co jde, připomeňme si, jak fungují hodnoty, které se měnit -nedají – např. čísla, řetězce, `True`, `False`, `None`. - -Vyzkoušej si následující kousek kódu. Co je na něm „špatně“? - -```python -kamaradka = 'Žaneta' -print(kamaradka) -kamaradka.upper() -print(kamaradka) -``` - -Proměnná `kamaradka` obsahuje řetězec `'Žaneta'` (který se už nedá změnit). -Metoda `upper()` vytvoří a vrátí *nový* řetězec `'ŽANETA'`. -Výsledná hodnota se ale v našem programu nevyužije – Python ji vytvoří, -ale pak ji „zahodí“. - -Oprava je snadná: výsledek si ulož do proměnné. -Často budeš chtít takový výsledek uložit zpátky do původní proměnné: - -```python -kamaradka = kamaradka.upper() -``` - -Tímto přiřazením Python „zahodí“ původní hodnotu. -Od tohoto příkazu dál bude proměnná `kamaradka` označovat nový řetězec. - -Podobně by se dala proměnná přenastavit na jakoukoli jinou hodnotu: - -```python -kamaradka = 'Žaneta' -print(kamaradka) -kamaradka = 'Alexandra' -print(kamaradka) -``` - - -## Měnění seznamů - -A jak jsou na tom seznamy? -Ty se měnit dají. - -Základní způsob, jak změnit seznam, je přidání -prvku na konec pomocí metody `append`. -Ta *nic nevrací* (resp. vrací `None`), ale „na místě” (angl. *in place*) změní -seznam, se kterým pracuje. Vyzkoušej si to: - -```pycon ->>> zviratka = ['pes', 'kočka', 'králík'] ->>> print(zviratka) -['pes', 'kočka', 'králík'] ->>> zviratka.append('morče') ->>> print(zviratka) -['pes', 'kočka', 'králík', 'morče'] -``` - -Všimni si, že proměnná `zviratka` se nastavuje jen na začátku. -V rámci celého běhu programu výše existuje jen jeden seznam. -Na začátku má tři prvky, pak mu jeden přibude, ale stále je to jeden a ten -samý seznam. - -Takové měnění může být občas překvapující, -protože stejná hodnota může být přiřazená ve více proměnných. -Protože se mění hodnota samotná, může to vypadat, -že se „mění proměnná, aniž na ni sáhneš”: - -```python -a = [1, 2, 3] # Vytvoření seznamu -b = a # Tady se nový seznam nevytváří! - -# seznam vytvořený v prvním řádku má teď dvě jména: "a" a "b", -# ale stále pracuješ jenom s jedním seznamem - -print(b) -a.append(4) -print(b) -``` - - -## Další způsoby, jak měnit seznamy - -Kromě metody `append`, která přidává jediný prvek na konec, existuje -spousta dalších metod, které seznamy mění. -Všechny udělají změny přímo v daném seznamu a (kromě `pop`) vrací `None`: - -* `extend()` přidá více prvků najednou, -* `pop()` odebere poslední prvek a *vrátí ho* (jako návratovou hodnotu), -* `insert()` přidá prvek na danou pozici, -* `remove()` odstraní první výskyt daného prvku, -* `sort()` seznam seřadí (řetězce „podle abecedy”, čísla vzestupně), -* `clear()` odstraní všechny prvky. -* `reverse()` obrátí pořadí prvků, - -{{ figure(img=static('methods.svg'), alt="Tahák") }} - -Například: - -```python -zviratka = ['pes', 'kočka', 'králík'] -zviratka.append('morče') # ['pes', 'kočka', 'králík', 'morče'] -zviratka.insert(2, 'had') # ['pes', 'kočka', 'had', 'králík', 'morče'] -zviratka.pop() # ['pes', 'kočka', 'had', 'králík'], vrátí 'morče' -zviratka.remove('had') # ['pes', 'kočka', 'králík'] -zviratka.sort() # ['kočka', 'králík', 'pes'] -zviratka.reverse() # ['pes', 'králík', 'kočka'] -zviratka.clear() # [] -``` - -## Vybírání ze seznamů - -Často budeš ze seznamu chtít vybrat prvek na určité pozici. -To funguje jako u řetězců: do hranatých závorek dáš číslo prvku. -Stejně jako u řetězců se čísluje od nuly a záporná čísla číslují od konce. - -```python -zviratka = ['pes', 'kočka', 'králík'] -print(zviratka[2]) -``` - -Hranatými závorkami můžeš získat i podseznam. -[Diagram z materiálů k řetězcům]({{ lesson_url('beginners/str-index-slice')}}#slicing-diagram) -ukazuje, jak u takového „sekání” číslovat: -funguje to stejně, jen místo menšího řetězce dostaneš menší seznam. - -```python -zviratka = ['pes', 'kočka', 'králík', 'had', 'andulka'] -print(zviratka[1:-2]) -``` - -„Sekáním“ vzniká nový seznam – když pak původní seznam změníš, v novém menším seznamu se -to neprojeví. - - -### Měnění prvků - -Na rozdíl od řetězců (které se měnit nedají) můžeš u existujících seznamů -nastavovat konkrétní prvky – a to tak, že do prvku přiřadíš jako by to byla -proměnná: - -```python -zviratka = ['pes', 'kočka', 'králík'] -zviratka[1] = 'koťátko' -print(zviratka) -``` - -Přiřazovat se dá i do podseznamu – v tomto případě -se podseznam nahradí jednotlivými prvky z toho, -co přiřadíš. - -```python -zviratka = ['pes', 'kočka', 'králík', 'had', 'andulka'] -print(zviratka[1:-2]) -zviratka[1:-2] = ['koťátko', 'králíček'] -print(zviratka) -``` - -### Mazání prvků - -Přiřazením do podseznamu můžeš i změnit délku -seznamu nebo některé prvky úplně odstranit: - -```python -zviratka = ['pes', 'kočka', 'králík'] -zviratka[1:-1] = ['had', 'ještěrka', 'drak'] -print(zviratka) -zviratka[1:-1] = [] -print(zviratka) -``` - -Tenhle zápis pro mazání prvků je ale docela nepřehledný. -Proto na to existuje zvláštní příkaz jménem `del`. -Jak už jeho název (z angl. *delete*, smazat) -napovídá, smaže, co mu přijde pod ruku – jednotlivé -prvky seznamů, podseznamy, … a dokonce i proměnné! -Zkus si: - -```python -zviratka = ['pes', 'kočka', 'králík', 'had', 'ještěrka', 'andulka'] - -# Smazání prvku seznamu -print(zviratka[-1]) -del zviratka[-1] -print(zviratka) - -# Smazání podseznamu -print(zviratka[1:-1]) -del zviratka[1:-1] -print(zviratka) -``` - -> [note] -> Příkaz `del` umí mazat i proměnné. -> Po takovém smazání se proměnná chová jako kdyby do ní nikdo nikdy nic -> nepřiřadil: -> -> ```python -> del zviratka -> print(zviratka) -> ``` -> -> V praxi se ale `del` na proměnné příliš často nepoužívá. - - -Na mazání prvků můžeš použít i metody zmíněné výše: -* `pop` odstraní poslední prvek v seznamu a *vrátí* ho, -* `remove` najde v seznamu první výskyt daného prvku a odstraní ho, -* `clear` vyprázdní celý seznam. - -```python -balicek = ['eso', 'sedma', 'svršek', 'sedma', 'král'] -liznuta_karta = balicek.pop() -print(liznuta_karta) -print(balicek) - -balicek.remove('sedma') -print(balicek) - -balicek.clear() -print(balicek) -``` - -## Řazení - -Metoda `sort` seřadí prvky seznamu. - -```python -seznam = [4, 7, 8, 3, 5, 2, 4, 8, 5] -seznam.sort() -print(seznam) -``` - -Aby se daly seřadit, musí být prvky seznamu vzájemně -*porovnatelné* – konktrétně na ně musí fungovat operátor `<`. -Seznam s mixem čísel a řetězců tedy seřadit nepůjde. -Operátor `<` definuje i jak přesně `sort` řadí: čísla vzestupně podle -velikosti; řetězce podle speciální „abecedy” která řadí -malá písmena za velká, česká až za anglická, atd. - -Metoda `sort` zná pojmenovaný argument `reverse`. -Pokud ho nastavíš na *True*, řadí se naopak – od největšího prvku po nejmenší. - -```python -seznam = [4, 7, 8, 3, 5, 2, 4, 8, 5] -seznam.sort(reverse=True) -print(seznam) -``` - -## Známé operace se seznamy - -Spousta toho, co můžeš dělat s řetězci, má stejný účinek i u seznamů. -Třeba sečítání a násobení číslem: - -```python -melodie = ['C', 'E', 'G'] * 2 + ['E', 'E', 'D', 'E', 'F', 'D'] * 2 + ['E', 'D', 'C'] -print(melodie) -``` - -Stejně jako u řetězců jde se seznamem sečíst jen další seznam. -Nemůžeš sečítat třeba seznam s řetězcem. - -Další staří známí jsou funkce `len`, -metody `count` a `index`, a operátor `in`. - -```python -print(len(melodie)) # Délka seznamu -print(melodie.count('D')) # Počet 'D' v seznamu -print(melodie.index('D')) # Číslo prvního 'D' -print('D' in melodie) # Je 'D' v seznamu? -``` - -Poslední tři se ale přece jen chovají kapku jinak: -u řetězců pracují s *podřetězci*, -u seznamů jen s *jednotlivými* prvky. -Takže ačkoliv melodie výše obsahuje prvky -`'D'` a `'E'` vedle sebe, `'DE'` ani `['D', 'E']` v seznamu není: - -```python -print('DE' in melodie) -print(melodie.count('DE')) -print(melodie.index('DE')) -``` - -## Seznam jako podmínka - -Seznam můžeš použít v příkazu `if` (nebo `while`) jako podmínku, -která platí, když v tom seznamu něco je. -Jinými slovy, `seznam` je tu „zkratka“ pro `len(seznam) > 0`. - -```python -if seznam: - print('V seznamu něco je!') -else: - print('Seznam je prázdný!') -``` - -Podobně můžeš v podmínce použít i řetězce. -A dokonce i čísla – ta jako podmínka platí, pokud jsou nenulová. - -## Tvoření seznamů - -Tak jako funkce `int` převádí na -celá čísla a `str` na řetězce, -funkce `list` převádí na seznam. -Jako argument jí můžeš předat jakoukoli hodnotu, -kterou umí zpracovat příkaz `for`. -Z řetězce udělá seznam znaků, z `range` udělá seznam čísel. - -```python -abeceda = list('abcdefghijklmnopqrstuvwxyz') -cisla = list(range(100)) -print(abeceda) -print(cisla) -``` - -I ze seznamu udělá funkce `list` seznam. -To může znít zbytečně, ale není – vytvoří se totiž *nový* seznam. -Bude mít sice stejné prvky ve stejném pořadí, -ale nebude to ten samý seznam: -měnit se bude nezávisle na tom starém. - -```python -a = [1, 2, 3] -b = list(a) - -print(b) -a.append(4) -print(b) -print(a) -``` - -Další způsob, jak tvořit seznamy -(zvláště složitější), je nejdřív udělat prázdný -seznam a pak ho postupně naplnit pomocí funkce `append`. -Třeba pokud z nějakého důvodu chceš seznam -mocnin dvou, projdi čísla, kterými chceš mocnit, -cyklem `for` a pro každé z nich -do seznamu přidej příslušnou mocninu: - -```python -mocniny_dvou = [] -for cislo in range(10): - mocniny_dvou.append(2 ** cislo) -print(mocniny_dvou) -``` - -Podobným způsobem získáš seznam `matka`, `babička`, `prababička`, -`praprababička`, atd.: - -```python -predkove = ['matka'] -for pocet_pra in range(10): - predkove.append(('pra' * pocet_pra) + 'babička') -print(predkove) -``` - -Chceš-li seznam, který reprezentuje balíček karet, -zavolej `append` pro všechny kombinace barev a hodnot. -Neboli česky: - -* Začni s prázdným **balíčkem**. -* Pro každou ze čtyř **barev** (*přidáme 13 karet té barvy, a to následovně*): - * Pro každou ze 13 **hodnot**, 2-10 a 4 karty s obrázkem: - * Přidej do **balíčku** kartu s danou **barvou** a **hodnotou**. -* **Balíček** je hotový, vypiš ho. - -Takový program může být trochu složitější vymyslet. -Začít můžeš programem, který všechny karty jen vypíše: - -```python -for barva in '♠', '♥', '♦', '♣': - for hodnota in ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']: - print(hodnota, barva) -``` - -A pak program změň tak, aby každou kartu místo vypsání přidal do seznamu: - -```python -balicek = [] -for barva in '♠', '♥', '♦', '♣': - for hodnota in ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']: - balicek.append(hodnota + barva) -print(balicek) -``` - -Výsledný program porovnej s českým „překladem“ výše. - - -> [note] Jde to líp? -> Psát do programu výčet po sobě jdoucích čísel, -> `'2', '3', '4', '5', '6', '7', '8', '9', '10'`, -> není ideální – na takovou otročinu přece máme počítače! -> Zkus čísla dostat pomocí `range`. -> Ale pozor, není to úplně přímočaré: -> -> * Jaké argumenty dáš funkci `range`, abys dostal{{a}} čísla od 2 do 10? -> * Funkce `range` vrací sekvenci, která ale není seznam. -> Abys ji mohl{{a}} spojit se seznamem `['J', 'Q', 'K', 'A']`, budeš ji muset -> na seznam převést: `list(range(...))` -> * Abys mohl{{a}} čísla z `range` připojit k řetězci jako `♠`, budeš muset -> každou hodnotu před použitím převést na řetězec: `str(hodnota)`. -> -> Bonus: Jaký je nejkratší zápis, kterým můžeš zadat seznam -> `['J', 'Q', 'K', 'A']`? -> -> Řešení najdeš v textu o kousek níže. - - -## Seznamy a řetězce - -Seznamy a řetězce jsou druhy *sekvencí*. -Můžeš různě převádět z jednoho typu na druhý. - -Funkce `list` vytvoří z řetězce seznam znaků. -Když chceš dostat seznam slov, použij -na řetězci metodu `split` (angl. *rozdělit*): - -```python -slova = 'Tato věta je složitá, rozdělme ji na slova!'.split() -print(slova) -``` - -Metoda `split` umí brát i argument. -Pokud ho předáš, řetězec „rozseká” podle daného oddělovače -(místo mezer a nových řádků). -Takže když máš nějaká data oddělená čárkami, -použij `split` s čárkou: - -```python -zaznamy = '3A,8B,2E,9D'.split(',') -print(zaznamy) -``` - -Chceš-li spojit seznam řetězců zase dohromady -do jediného řetězce, použij metodu `join` (angl. *spojit*). -Pozor, tahle metoda se volá na *oddělovači*, -tedy na řetězci, kterým se jednotlivé kousky „slepí” dohromady. -Seznam jednotlivých řetězců bere jako argument. - -```python -veta = ' '.join(slova) -print(veta) -``` - -## Seznamy a náhoda - -Modul `random` obsahuje funkce, které mají něco společného s náhodou: -třeba nám už známou `random.randrange`. -Podívejme se na dvě další, které se hodí k seznamům. - -Funkce `shuffle` seznam „zamíchá” – všechny prvky náhodně popřehází. -Seznam změní „na místě“ a nic nevrací, podobně jako metoda `sort`. - -```python -import random - -ciselne_hodnoty = list(range(2, 11)) -pismenne_hodnoty = list('JQKA') - -balicek = [] -for barva in '♠', '♥', '♦', '♣': - for hodnota in ciselne_hodnoty + pismenne_hodnoty: - balicek.append(str(hodnota) + barva) -print(balicek) - -random.shuffle(balicek) -print(balicek) -``` - -A funkce `choice` ze seznamu vybere jeden náhodný prvek. -S použitím seznamu tak můžeš třeba jednoduše vybrat tah pro hru -kámen/nůžky/papír: - -```python -import random -mozne_tahy = ['kámen', 'nůžky', 'papír'] -tah_pocitace = random.choice(mozne_tahy) -print(tah_pocitace) -``` diff --git a/lessons/beginners/list/info.yml b/lessons/beginners/list/info.yml deleted file mode 100644 index 831dbc92..00000000 --- a/lessons/beginners/list/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Seznamy -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/local-variables/index.md b/lessons/beginners/local-variables/index.md deleted file mode 100644 index 02933986..00000000 --- a/lessons/beginners/local-variables/index.md +++ /dev/null @@ -1,186 +0,0 @@ -# Lokální proměnné - -Už umíš definovat vlastní funkce. -Zbývá ale ještě dovysvětlit, jak v nich fungují proměnné. - -Funkce může používat proměnné „zvnějšku“. -Následující program přiřadí do proměnné `pi` a všechny další funkce -mají k `pi` přístup: - -```python -pi = 3.1415926 - -def obsah_kruhu(polomer): - return pi * polomer ** 2 - -print(obsah_kruhu(100)) -``` - -Jinak je tomu ale v případě, kdy proměnnou nastavíš *uvnitř* funkce. - -Všechny parametry a všechny proměnné, do kterých funkce přiřazuje, -jsou *úplně nové* proměnné, které nemají nic -společného s tím, co je „venku“ kolem funkce. - -Těmto proměnným se říká *lokální proměnné* (angl. *local variables*), -protože existují jen místně, v rámci volání jedné jediné funkce. - -Proměnné, které nejsou lokální, jsou *globální* – ty existují v celém programu. - -Pro příklad: - -```python -def nastav_x(hodnota): - x = hodnota # Přiřazení do lokální proměnné! - -nastav_x(40) -print('x =', x) -``` - -Program skončí s chybou! -Funkce `nastav_x` si hraje na vlastním písečku; proměnná `x` je jen -pro ni. -Když funkce `nastav_x` skončí, proměnná `x` přestane existovat. - - -## Skrývání detailů - -Podobně skončí s chybou i složitější program: - -```python -def zamen(slovo, pozice, novy_znak): - """V daném slově zamění znak na dané pozici za daný nový znak""" - zacatek = slovo[:pozice] - konec = slovo[pozice + 1:] - nove_slovo = zacatek + novy_znak + konec - return nove_slovo - -print(zamen('kočka', 1, 'a')) -print(zamen('kačka', 2, 'p')) - -print(zacatek) # NameError -``` - -Funkci `zamen` jsi napsal{{a}} proto, abys nemusel{{a}} pořád opakovat detaily -záměny písmenka. -Jakmile je jednou nadefinovaná, stačí ji zavolat. Důležité jsou jen jméno -funkce, parametry a návratová hodnota; na detaily kódu uvnitř můžeš zapomenout. -A to i díky lokálním proměnným, které detaily ve vnitřku funkce trochu líp -izolují od zbytku programu. - -Ještě lépe je to vidět u funkcí, které jsi nenapsal{{a}} {{gnd('sám', 'sama')}}. -Jak divné by bylo, kdyby po každém zavolání `print` byla najednou nastavená -proměnná `i`, kterou `print` náhodou používá při procházení svých parametrů! - - -## Přiřazení - -To, co dělá lokální proměnnou, je *přiřazení*. -Porovnej `nastav_x` s příkladem na `obsah_kruhu` výše: rozdíl mezi `pi` a `x` -je v tom, že do `x` se v rámci funkce přiřazuje. - -Co je to přiřazení? Všechno, co *nastavuje* nějakou proměnnou. Například: -* Klasika je přiřazovat pomocí `=`, např. `a = 3`. -* Parametry funkce: funkce `def nastav_x(hodnota)` přiřadí do `hodnota`, -* Cyklus `for x in ...:` přiřazuje do proměnné `x`. -* Pro úplnost, příkazy `def x(...):`, `import x` a `from ... import x` taky - přiřazují do `x` – ale ve funkcích se moc nepoužívají. - -> [note] A další -> K těmto materiálům se možná budeš vracet, tak pro úplnost přidám další -> způsoby, které postupně poznáš. Není jich mnoho: -> * Příkazy `with ... as x`, `del x`, `except ... as x` přiřazují do `x`. -> * Přiřazují i speciální přiřazovací operátory jako `+=`, `*=`, `:=`. - - -## Zakrývání jména - -Jak to funguje, když ve funkci přiřadíš do proměnné, která existuje i globálně? -Pak tu máme problém. - -Vytvoří se *úplně nová* lokální proměnná, která má stejné jméno jako -ta globální. -Jméno označuje lokální proměnnou, a ta globální pak „není vidět“. - -Tento příklad tedy nebude fungovat tak, jak se zdá: - -```python -x = 0 - -def nastav_x(hodnota): - x = hodnota # Přiřazení do lokální proměnné! - print('Ve funkci nastav_x: x =', x) - -nastav_x(40) -print('Venku: x =', x) -``` - -V tomto programu existují *dvě* proměnné jménem `x`. -Jedna je globální. Jedna je lokální pro funkci `nastav_x`. -Jmenují se stejně, ale jsou to dvě různé proměnné. - - -## Lokální nebo globální? - -Pojďme si to ukázat. -Než spustíš tenhle program, zkus předpovědět, co bude dělat. -Pak ho pusť, a pokud dělal něco jiného, zkus vysvětlit proč. -Pozor na chytáky! - -```python -from math import pi -obsah = 0 -a = 30 - -def obsah_elipsy(a, b): - obsah = pi * a * b # Přiřazení do `obsah` - a = a + 3 # Přiřazení do `a` - return obsah - -print(obsah_elipsy(a, 20)) -print(obsah) -print(a) -``` - -Zkus odpovědět na tyto otázky: - -* Je proměnná `pi` lokální, nebo globální? -* Je proměnná `obsah` lokální, nebo globální? -* Je proměnná `a` lokální, nebo globální? -* Je proměnná `b` lokální, nebo globální? -* Je proměnná `obsah_elipsy` lokální, nebo globální? - -{% filter solution %} -* `pi` je globální – nepřiřazuje se do ní ve funkci; - je „vidět“ v celém programu. -* Proměnné `obsah` jsou v programu dvě – jedna globální, - a jedna je lokální pro funkci `obsah_elipsy`, - protože do ní tahle funkce přiřazuje. -* Proměnné `a` jsou taky dvě, podobně jako `obsah`. - Tady byl chyták: příkaz `a = a + 3` nemá žádný smysl; - do `a` se sice uloží větší číslo, ale vzápětí funkce `obsah_elipsy` skončí - a její lokální proměnná `a` přestane existovat. -* Proměnná `b` je jenom lokální – jako parametr funkce `obsah_elipsy`. -* Proměnná `obsah_elipsy` je globální (a je v ní funkce). - -> [note] A pro úplnost -> -> * Klíčová slova `from`, `import`, `def`, `return` neoznačují proměnné. -> * Jméno modulu `math` taky neoznačuje proměnnou. -> * Proměnná `print` se dá považovat za globální. -> (Ve skutečnosti existuje zvláštní kategorie *zabudovaných* (angl. *builtin*) -> proměnných – ty jsou „ještě globálnější“.) - -{% endfilter %} - - -## Rada na závěr - -Pravidla pro lokální proměnné jsou pro začátečníky jednou z nejzvláštnějších -věcí v Pythonu. -Jsou ale přínosná – umožňují některé užitečné techniky, např. rekurzi. - -Jestli ti to celé připadá složité, dá se tomu zatím vyhnout dodržováním jednoho -pravidla: -*nepřiřazuj ve funkcích do proměnných, které existují i vně funkce.* -(Parametr funkce se počítá jako přiřazení.) diff --git a/lessons/beginners/local-variables/info.yml b/lessons/beginners/local-variables/info.yml deleted file mode 100644 index f1ac8217..00000000 --- a/lessons/beginners/local-variables/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Lokální proměnné -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/main-module/index.md b/lessons/beginners/main-module/index.md deleted file mode 100644 index f2ffe54b..00000000 --- a/lessons/beginners/main-module/index.md +++ /dev/null @@ -1,166 +0,0 @@ -# Spouštěcí moduly - -Automatické testy musí projít „bez dozoru“. -V praxi se často automaticky spouští, případné chyby se automaticky -oznamují (např. e-mailem) a fungující otestovaný kód se automaticky -začne používat dál (nebo se rovnou vydá zákazníkům). - -Co to znamená pro nás? -Funkce `input` v testech nefunguje. Nemá koho by se zeptala; „za klávesnicí“ -nemusí nikdo sedět. - -To může někdy „ztěžovat práci“. Ukážeme si to na složitějším projektu: -na Kámen-Nůžky-Papír. - -Kód pro Kámen-Nůžky-Papír může, velice zjednodušeně, vypadat zhruba takto: - -```python -import random # (příp. import jiných věcí, které budou potřeba) - -tah_pocitace = 'kámen' -tah_hrace = input('Co chceš hrát (kámen, nůžky, papír)? ') - -# (tady reálně bude spousta zanořených ifů) -if tah_hrace == 'papír': - print('Vyhrál{{a}} jsi!') -else: - print('Nevyhrál{{a}} jsi...') - -``` - -Když tenhle modul naimportuješ, Python v něm postupně, odshora dolů, -provede všechny příkazy. - -První příkaz, `import`, jen zpřístupní nějaké proměnné a funkce; -je-li importovaný modul správně napsaný, nemá vedlejší účinek. -Definice funkcí (příkazy `def` a všechno v nich) podobně jen definují funkce. -Ale zavoláním funkce `input` se spustí interakce: program potřebuje vstup -od uživatele. - -Importuješ-li tenhle modul z testů, `input` selže a import se nepovede. - -> [note] -> A kdybys modul importoval{{a}} odjinud – například bys chtěl{{a}} tuhle -> funkčnost použít v nějaké jiné hře – uživatel si bude muset v rámci importu -> zahrát Kámen-Nůžky-Papír! - -Volání funkce `input` je vedlejší efekt. -Je potřeba ho odstranit. -Importovatelné moduly by měly pouze dát k dispozici nějaké funkce nebo hodnoty. -Dej tedy hru do funkce: - -```python -# knp.py -- importovatelný modul - -import random # (příp. import jiných věci, které budou potřeba) - -def hrej_hru(): - tah_pocitace = 'kámen' - tah_hrace = input('Co chceš hrát (kámen, nůžky, papír)? ') - - # (tady reálně bude spousta zanořených ifů) - if tah_hrace == 'papír': - print('Vyhrál{{a}} jsi!') - else: - print('Nevyhrál{{a}} jsi...') - -``` - -No jo, ale po takovém odstranění -už nejde jednoduše spustit hra! Co s tím? - -Můžeš na to vytvořit nový modul, ve kterém bude jenom volání funkce: - -```python -# hra.py -- spouštěcí modul - -import knp - -knp.hrej_hru() -``` - -Tenhle modul nebudeš moci testovat (protože nepřímo volá funkci `input`), -ale můžeš ho spustit, když si budeš chtít zahrát. -Protože k němu nemáš napsané testy, nepoznáš -z nich, když se takový spouštěcí modul rozbije. -Spouštěcí modul by proto měl být co nejjednodušší – jeden import a jedno volání. - -Původní modul teď můžeš importovat bez obav – ať už z testů nebo z jiných -modulů. -Pořád se ale, kvůli funkcím `input` a `print`, špatně testuje. -Aby se testoval líp, můžeš kousek funkčnosti dát do jiné funkce: - -```python -# knp.py -- importovatelný modul - -import random # (příp. import jiných věci, které budou potřeba) - -def vyhodnot(tah_pocitace, tah_hrace): - # (tady reálně bude spousta zanořených ifů) - if tah_hrace == 'papír': - return 'Vyhrál{{a}} jsi!' - else: - return 'Nevyhrál{{a}} jsi...' - - -def hrej_hru(): - tah_pocitace = 'kámen' - tah_hrace = input('Kam chceš hrát?') - - vysledek = vyhodnot(tah_pocitace, tah_hrace) - print(vysledek) -``` - -A vida! Funkce `vyhodnot` teď neobsahuje ani `print` ani `input`. -Půjde tedy docela jednoduše otestovat: - -```python -# test_knp.py -- testy - -import knp - -def test_vyhry(): - assert vyhodnot('kámen', 'papír') == 'Vyhrál{{a}} jsi!' - assert vyhodnot('papír', 'nůžky') == 'Vyhrál{{a}} jsi!' - assert vyhodnot('nůžky', 'kámen') == 'Vyhrál{{a}} jsi!' -``` - -Funkce `hrej_hru` ovšem tak dobře otestovat nejde. -Musíš ji testovat ručně. -Protože ale hlavní část programu (`vyhodnot`) jde pokrýt automatickými testy, -ruční testování nemusí být tak důkladné. - - - -## Negativní testy - -Test `test_vyhry`, ukázaný výše, není úplný. -Splnila by ho i funkce jako: - -```python -def vyhodnot(tah_pocitace, tah_hrace): - return 'Vyhrál{{a}} jsi!' -``` - -Kromě „pozitivních“ výsledků je potřeba kontrolovat i ty „negativní“: -ať už očekávaný negativní výsledek (jako prohru nebo remízu) -nebo reakci programu na špatné nebo neočekávané podmínky. - -Co třeba má dělat volání `vyhodnot(8, 'kukačka')`? - -Testy, které kontrolují reakci na „špatný“ vstup, -se jmenují *negativní testy*. -Často kontrolují to, že nastane nějaká výjimka. - -Na otestování výjimky použij příkaz `with` a funkci `raises` naimportovanou -z modulu `pytest`. -Jak příkaz `with` přesně funguje, to se dozvíš později; -teď stačí říct, že `with pytest.raises` ověří, že odsazený blok kódu -pod ním vyvolá danou výjimku: - -```python -def test_spatneho_tahu(): - """🤘 vs. 🖖 není správný vstup""" - with pytest.raises(ValueError): - vyhodnot('metal', 'spock') -``` diff --git a/lessons/beginners/main-module/info.yml b/lessons/beginners/main-module/info.yml deleted file mode 100644 index c620c352..00000000 --- a/lessons/beginners/main-module/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Spouštěcí moduly a Negativní testy -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2020. -license: cc-by-sa-40 diff --git a/lessons/beginners/micropython/index.md b/lessons/beginners/micropython/index.md deleted file mode 100644 index 8f45cc19..00000000 --- a/lessons/beginners/micropython/index.md +++ /dev/null @@ -1,829 +0,0 @@ -# MicroPython na malém zařízení - -> [note] Tahle sekce bohužel nejde jednoduše projít z domu. -> Využíváme speciální vybavení, které je potřeba nejdřív -> sehnat. Máš-li možnost se dostat na sraz, nebo -> aspoň kontaktovat organizátory, doporučujeme shánět -> spíš tímto způsobem. -> Případně jde případný hardware objednat přes Internet, -> typicky z čínských e-shopů. - -> [note] -> Materiály byly připraveny pro celodenní workshop; -> na kratší lekcích může být něco vynecháno. - -{{ figure( - img=static('nodemcu-devkit.jpg'), - alt='LoLin NodeMCU v3 – Vývojová deska s čipem ESP8266', - float='right', -) }} - -Dnes budeme programovat malé zařízení – -tak malé, že ho pohodlně schováš v ruce. -Konkrétně budeme používat „chytrou destičku”, modul zvaný -*NodeMCU Devkit*, která by měla ležet před tebou. -Než ji vyndáš z obalu, měla by ses *vybít*: -dotkni se něčeho kovového, co je spojeno se zemí, -třeba radiátoru nebo kovové části schránky nějakého -spotřebiče, který je zapojený do zásuvky. -Tím se zbavíš statické elektřiny, která by mohla -malinké zařízení poškodit. -Pak přístroj vyndej z obalu. Snaž se ho držet za -hrany a příliš se nedotýkat elektroniky a kovových -částí. - -> [note] -> Obal bude nejspíš roztržený, protože organizátoři -> na destičku před začátkem kurzu nainstalovali -> MicroPython. - -Teď, když destičku držíš v ruce, si -pojďme projít její základní součásti. - -
- -{{ figure( - img=static("nodemcu-popisky.svg"), - alt='Obrázek desky NodeMCU DevKit', - float='left', -) }} - -Nejdůležitější část vývojové desky je v oplechované -krabičce s logem "Wi-Fi" a "FCC": -mikroprocesor ESP8266. -To je „mozek” celého zařízení, který – když je -správně naprogramován – umí provádět pythonní -příkazy a programy. -Procesor sedí na malé destičce, na které je ještě -anténa, kterou -přístroj může komunikovat s okolím. - -Tahle malá destička se dá použít i samostatně; -všechno ostatní, co kolem ní zabírá tolik místa, -nám jen ulehčí hraní a umožní se zařízením -jednoduše komunikovat a krmit ho elektřinou. - -Komunikace a „krmení” se děje přes -μUSB konektor, -do kterého zapojíš kabel ze svého počítače. -Když je modul naprogramovaný, stačí ho místo do -počítače zapojit do nabíječky či externího zdroje -(powerbanky) a bude fungovat samostatně. - -Kolem USB konektoru jsou dvě tlačítka: -RST, kterým se destička restartuje -(skoro jako kdybys ho odpojila a zase zapojila, což -se hodí, když něco uděláš špatně a modul „zamrzne”), -a FLASH, o kterém si povíme později. - -Po stranách modulu jsou dvě řady -„nožiček”, na které -se dá napojit celá řada nejrůznějších hraček. -Zkontroluj si, jestli jsou všechny nožičky rovné; -kdyby byla některá ohnutá, tak ji (nejlépe s pomocí -kouče) narovnej nebo si vezmi jinou destičku. - -
- - -## Instalace - -Bohužel se dnes neobejdeme bez instalace. Musíš naučit -svůj počítač, aby si s destičkou povídal. - -Nejdřív si do virtuálního prostředí nainstaluj program Ampy od Adafruitu. -Ten budeme později používat na nahrávání kódu: - -```console -(env)$ python -m pip install adafruit-ampy -``` - -Pak propoj modul s počítačem přes USB kabel, -jako kdybys připojoval{{a}} třeba mobil. - -> [note] -> Je potřeba použít kvalitní datový kabel. -> Nekvalitní kabely (např. spousta kabelů k -> nabíječkám) jsou často nepoužitelné. - -Dál postupuj podle operačního systému na svém počítači. -Kdyby něco nefungovalo, poraď se s koučem. -Původní (anglický) návod k této části je na -stránkách MicroPythonu. - - -### Linux - -Na správně nastaveném počítači stačí zadat: - -```console -$ picocom -b 115200 --flow n /dev/ttyUSB0 -``` - -Pokud příkaz neskončí s chybou, stiskni tlačítko `RST` na modulu. -Měly by se nakonec objevit tři zobáčky, `>>>`. - -Většina počítačů ale na komunikaci s malými zařízeními nastavená není. -Skončí-li příkaz `picocom` s chybou, -oprav ji podle následujícího návodu a zkus to znova. -(Možná bude potřeba vyřešit víc než jednu chybu.) - -* Nemáš-li příkaz `picocom` nainstalovaný, - je potřeba ho nainstalovat (např. - `sudo dnf install picocom` nebo - `sudo apt-get install picocom`). -* Pokud `picocom` skončil s chybou - `No such file or directory`, pravděpodobně - je potřeba k zařízení přistupovat přes jiný soubor. - Použij příkaz `dmesg | tail`, který vypíše něco jako: - - {# XXX: ttyUSB0 should be highlighted #} - ```console - $ dmesg | tail - [703169.886296] ch341 1-1.1:1.0: device disconnected - [703176.972781] usb 1-1.1: new full-speed USB device number 45 using ehci-pci - [703177.059448] usb 1-1.1: New USB device found, idVendor=1a86, idProduct=7523 - [703177.059454] usb 1-1.1: New USB device strings: Mfr=0, Product=2, SerialNumber=0 - [703177.059457] usb 1-1.1: Product: USB2.0-Serial - [703177.060474] ch341 1-1.1:1.0: ch341-uart converter detected - [703177.062781] usb 1-1.1: ch341-uart converter now attached to ttyUSB0 - ``` - - Máš-li místo `ttyUSB0` něco jiného, v příkazu `picocom` to použij místo - `ttyUSB0`. - -* Pokud `picocom` skončil s chybou `Permission denied`, potřebuješ získat - přístup k souboru zařízení. - To znamená přidat se do příslušné skupiny: - - ```console - $ sudo usermod -a -G dialout $(whoami) - ``` - - Poté je potřeba se znovu přihlásit, třeba příkazem: - - ```console - $ su - $(whoami) - ``` - - Pro ověření spusť příkaz `groups`; v jeho výstupu by mělo být `dialout`. - Například: - - ```console - $ groups - kristyna lp wheel dialout mock - ``` - - Kdyby to nefungovalo, na srazu ti může pomoci nějaký kouč. - Jestli procházíš materiály z domu a nepovedlo - se ti přidat do skupiny, dá se to obejít tak, - že místo `picocom` použiješ `sudo picocom`. - - -### Windows - -MicroPython se přihlásí jako COM port. Otevři -správce zařízení a zjisti, který COM port to je (kouč s tím pomůže). - -Nebylo-li zařízení nalezeno, je potřeba nainstalovat -*driver*, který je ke stažení třeba -[z tohoto blogu](http://kig.re/2014/12/31/how-to-use-arduino-nano-mini-pro-with-CH340G-on-mac-osx-yosemite.html), -případně z [materiálů pro Arduino workshop](https://onedrive.live.com/?authkey=%21AKLO1lLajaeTmo4&id=182EF9BC3A8BDDA4%2166743&cid=182EF9BC3A8BDDA4). - -Pak si nainstaluj si program -[PuTTY](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) -(`putty.exe`) a spusť ho. -V konfiguračním okýnku zaškrtni *Connection Type: Serial* a -do *Serial line:* zadej svůj COM port. -Pak přepni v seznamu vlevo na *Serial* a nastav *Speed* na *115200* -a *Flow Control* na *None*: - -{{ figure( - img=static("putty-config.jpg"), - alt='Obrázek nastavení PuTTY', -) }} -{# XXX: Better picture #} - -Potom zpátky v kategorii *Session* můžeš nastavení uložit pro příště: -do políčka *Saved Sessions* zadej *MicroPython* a klikni OK. - -Nakonec klikni *Open*. Mělo by se otevřít -okýnko podobné konzoli, kde se, když zmáčkneš -na modulu `RST`, objeví nakonec tři zobáčky: `>>>`. - - -### macOS - -V příkazové řádce zadej: - -```console -$ screen /dev/tty.usbmodem* 115200 -``` - -a stiskni Enter. -Pak na modulu zmáčkni `RST`. -Měly by se nakonec objevit tři zobáčky, `>>>`. - -Nejde-li to, je možná potřeba nainstalovat driver. Ten se dá stáhnout -z [tohoto blogu](https://tzapu.com/ch340-ch341-serial-adapters-macos-sierra/). - - -## MicroPython – taky Python - -Tak jako máš na počítači nainstalovaný operační -systém, na vývojové desce je takzvaný *firmware*, -program, který ovládá všechny ty drátky, -čipy a světýlka, co v ní jsou. -My používáme firmware zvaný *MicroPython*, -který navíc rozumí jazyku Python a umí provádět pythonní příkazy. Zkus si to! -Tři zobáčky, které vyskočily v minulém kroku, přišly -ze zařízení, které teď netrpělivě čeká na příkaz. - -```pycon ->>> 1+1 -2 ->>> print('Hello World') -Hello World -``` - -Téměř vše, co používáš v Pythonu na počítači, -umí MicroPython taky: čísla, řetězce, seznamy, třídy, -výjimky, moduly a tak dál. -Některé detaily ale jsou trochu osekané, aby se všechno -vešlo do extrémně malého prostoru. -Zkus si, jak se liší efekt následujících příkazů -od „velkého” Pythonu: - -```pycon ->>> print ->>> import math ->>> math.pi -``` - -Nejdůležitější věc, která je osekaná, je *standardní -knihovna* – většina modulů, které na -počítači můžeš naimportovat, v MicroPythonu chybí. -U modulů jako `turtle` je to pochopitelné, -ale v rámci šetření místem chybí i moduly jako `random`. -Většinou to příliš nevadí – malá zařízení se používají -na jiné věci než ty velké – ale je potřeba si na to -dát pozor. - -Některé věci ze standardní knihovny se dají najít -ve zjednodušené formě na jiných místech. -Například ačkoliv modul `random` chybí, -náhodné číslo od 0 do 255 se dá získat pomocí: - -```pycon ->>> from os import urandom ->>> urandom(1)[0] -61 -``` - -## Vstup - -MicroPython na malé destičce obsahuje některé -moduly, které jinde nenajdeš. Ten hlavní se jmenuje -`machine` a zpřístupňuje základní funkce zařízení. Zkus si: - -```python -from machine import Pin -pin = Pin(0, Pin.IN) -print(pin.value()) -``` - -Zmáčkni a drž tlačítko `FLASH` vedle USB konektoru. -Přitom pusť `print(pin.value())` znovu. -Jak se hodnota změní? - -Jak tomuhle kódu rozumět? -Třída `Pin` ti umožňuje ovládat jednotlivé -„nožičky”, kterými zařízení komunikuje s vnějším -světem: buď na nich nastavovat napětí, nebo zkoumat -jestli na nich nějaké napětí je. - -`Pin(0, Pin.IN)` vytvoří objekt třídy Pin, -který bude načítat data z „nožičky” číslo 0. -(`IN` znamená načítání – informace jdou *do* procesoru). -Funkce `pin.value()` změří napětí na dané -„nožičce” a vrátí buď 1 nebo 0 podle toho, jestli nějaké naměřila. - -No a „nožička” číslo 0 je připojená k tlačítku `FLASH`, -kterým se tak dá ono napětí ovládat. -Informace o tom, která nožička je kam připojená, -máš na [taháku](https://pyvec.github.io/cheatsheets/micropython/nodemcu-cs.pdf) – -můžeš si zkontrolovat, že Pin(0) u sebe má poznámku FLASH. - - -## Obvod - -Teď na chvíli necháme programování a postavíme si elektrický obvod. -Vezmi si modrou svítivou diodu (LED, „světýlko”) a -nepájivé pole („hloupou destičku”). -Zkusíme světýlko rozsvítit. - -LED rozsvítíš tak, že ji připojíš ke *zdroji napětí*, například k baterce. - -Jako zdroj napětí můžeme použít i náš modul. -Ten bere elektřinu přes USB a dává nám ji k dispozici -na některých svých „nožičkách”: -konkrétně plus na nožičce označené `3V` -a mínus na nožičce označené `G`. -Na tyhle nožičky musíš zapojit diodu. - -Připojování diody má jeden háček: -musíš ji zapojit správným směrem – plus na plus, mínus na mínus. -Opačně dioda svítit nebude. Dobrá zpráva je, že -když diodu otočíš špatně, nic se jí nestane. - -> [note] -> Základní vlastnost *diody* je ta, že pustí -> elektrický proud jen jedním směrem. Svítící dioda -> – *angl. Light Emitting Diode, LED* – ještě k -> tomu navíc svítí. - -Je potřeba rozpoznat rozdíl mezi nožičkami diody. -*Katoda* (`-`) je ta kratší nožička. -Pouzdro diody je u katody trochu seříznuté -a vevnitř v pouzdře, když se pozorně podíváš, uvidíš -u katody větší plíšek. -Té druhé nožičce se říká anoda (`+`). - -Tak, teď víš, kam diodu zapojit: katodu (kratší nožičku) -na `G` a anodu na `3V`. - -Držení nožiček diody u nožiček modulu by ti nejspíš -zaměstnalo obě ruce. Aby sis je uvolnila, použij -*nepájivé pole* (angl. *breadboard*). -Je v něm spousta dírek, do kterých se dají strkat dráty. -V rámci každé poloviny destičky je každá řada dírek – -tedy každá pětice – spojená dohromady. -Když zapojíš drátky do stejné řady, spojíš je tím. - -Zasuň modul do nepájivého pole. Pak připoj katodu -do dírky ve stejné řadě, kde je nožička -`3V` modulu, a podobně anodu k `G`. -Mělo by to vypadat jako na tomto obrázku: - -{{ figure( - img=static("circuits/led_bb.svg"), - alt="diagram zapojení", -) }} - -Potom zapoj USB kabel. Dioda by se měla rozsvítit! - -Zkus si, co se stane, když obě nožičky diody zapojíš ke `G`. - -{{ figure( - img=static("circuits/led_bb_off.svg"), - alt="diagram zapojení", -) }} - -Aby dioda svítila, musí být připojená na dvě místa, -mezi kterými je takzvaný *potenciálový rozdíl* — napětí. -Na nožičce `G` je 0 voltů; na nožičce -`3V` jsou 3,3 volty – je tedy mezi nimi rozdíl 3,3 V, přesně tolik, -kolik modrá LED potřebuje ke svícení. - -> [note] -> Samotná hodnota napětí nedává smysl – například -> říct, že je na jednom místě 3,3 V je nepřesné. -> Hodnota ve voltech se vždycky musí k něčemu vztahovat; -> vyjadřuje rozdíl mezi dvěma místy. -> V elektronice používáme rozdíl oproti „zemi” – napětí -> na nožičce `G`. Stanovíme si, že tam je -> 0 voltů a ostatní napětí počítáme vzhledem k ní. -> Na nožičce `3V` je tedy napětí 3,3 V vzhledem k zemi. - - -## Výstup - -Proč jsme diodu na to, aby se rozsvítila, -připojili k modulu a ne jen k baterce? -Ten modul je trošku složitější zařízení než baterka a jedna důležitá věc, -kterou umí navíc, je nastavovat napětí na různých nožičkách. -Umí zařídit, aby se nožička chovala jednou jako `3V` a jindy jako `G`. -Když připojíš diodu mezi `G` a takovou -přepínatelnou nožičku, můžeš nastavit, kdy svítí a kdy ne. - -Přepoj anodu diody z `3V3` na `D5`. Katodu nech na `G`. - -Máš-li zapojeno, znovu se připoj k MicroPythonu a zadej následující kód: - -```python -from machine import Pin -pin = Pin(14, Pin.OUT) -pin.value(0) -pin.value(1) -``` - -Když objekt Pin vytvoříš s `Pin.OUT`, MicroPython na něm bude nastavovat -napětí – buď 3,3 V (`value(1)`) nebo 0 V (`value(0)`). -A tak se dá s diodou blikat. - -> [note] -> Číslování nožiček je bohužel dvojí – nožička -> označená jako `D5` má v procesoru přiřazené číslo 14. -> Třída `Pin` v MicroPythonu používá číslování procesoru. -> Naštěstí máš [tahák](https://pyvec.github.io/cheatsheets/micropython/nodemcu-cs.pdf), -> kde snadno dohledáš že `D5` a `Pin(14)` jsou dvě jména stejné nožičky. - -Zvládneš napsat program, který zařídí, aby dioda -svítila když je zmáčknuté tlačítko `FLASH`, jinak ne? - -> [note] -> Nápověda: Můžeš pořád dokola zjišťovat stav tlačítka -> a nastavovat podle něj stav LED. - -{% filter solution %} -```python -from machine import Pin -pin_diody = Pin(14, Pin.OUT) -pin_tlacitka = Pin(0, Pin.IN) -while True: - pin_diody.value(1 - pin_tlacitka.value()) -``` -{% endfilter %} - - -## Pouštění kódu ze souboru - -Jak začneš psát trochu složitější programy, -mohlo by se stát, že tě konzole MicroPythonu začne trochu štvát. -Špatně se v ní opravují chyby a automatické odsazování funguje jen většinou. -Pojďme se podívat, jak naštvání předejít. - -Doporučuji si větší kousky kódu – a určitě takové, -ve kterých je nějaký cyklus, podmínka či funkce – -psát v textovém editoru a do modulu pak posílat celý soubor. - -Zkus si to. Do souboru `led_podle_tlacitka.py` dej následující kód: - -```python -from machine import Pin -from time import sleep -pin_diody = Pin(14, Pin.OUT) -while True: - pin_diody.value(0) - sleep(1/2) - pin_diody.value(1) - sleep(1/2) -``` - -Potom zavři konzoli (`picocom`, PuTTY nebo `screen`). - -K pouštění programu použijeme `ampy`, který jsi nainstaloval{{a}} dříve. -Ke spuštění budeš potřebovat znát port: - -* Linux: port používáš v příkazu `picocom`, např. `/dev/ttyUSB0` -* Windows: port používáš v PuTTY, např. `COM13` -* macOS: port používáš v příkazu `screen`, např. `/dev/tty.usbmodem*` - -`ampy` spusť následujícím příkazem, jen za `PORT` doplň svůj port: - -```console -(venv)$ ampy -p PORT run led_podle_tlacitka.py -``` - -Program by měl blikat diodou. -Využívá k tomu funkci `time.sleep()`, která počká daný počet vteřin – -tedy `time.sleep(1/2)` zastaví program na půl sekundy. - - -## Velice rychle blikat - -Jedna z nevýhod „našeho” čipu ESP8266 je, že na svých -nožičkách umí nastavovat jen dvě hodnoty – 3,3 V a zem, jedničku a nulu. -Dioda tak buď svítí, nebo nesvítí – nedá se -nastavit poloviční intenzita, nedá se plynule rozsvěcet nebo zhasínat. - -Tuhle nevýhodu ale můžeme obejít s využitím dvou faktů. -Ten první je, že diodám – na rozdíl od žárovek nebo -zářivek – nevadí časté vypínání a zapínání. -Opotřebovávají se spíš svícením a časem. -Druhý je, že lidské oko nestačí zaznamenat pohyby a -změny, které probíhají rychleji než zhruba za -setinu vteřiny. - -Pojďme tedy velice rychle blikat – a oblafnout tak naše oči a mozky! - -```python -from machine import Pin -from time import sleep -pin_diody = Pin(14, Pin.OUT) -while True: - pin_diody.value(0) # vypnout LED - sleep(2/100) # počkat dvě setiny vteřiny - pin_diody.value(1) # zapnout LED - sleep(1/100) # počkat jednu setinu vteřiny -``` - -Zkus si pohrát s hodnotami pro `time.sleep`. - -> [note] -> Takhle fungují prakticky všechna stmívatelná LED -> světla – rychlé blikání je ekonomičtější a přesnější -> než např. nastavování nižšího napětí. - -Dokážeš napsat program, který diodu postupně, plynule rozsvítí? - - - -Protože je takovéhle rychlé blikání užitečné ve spoustě -různých situací, obsahuje MicroPython speciální funkci: umí blikat samostatně. -Nastavíš, jak rychle má blikat a jak dlouho má trvat -každé bliknutí, a MicroPython pak bude blikat automaticky, -zatímco tvůj program se může věnovat něčemu jinému. - -Téhle funkci se říká *pulzně šířková modulace* – -angl. *Pulse Width Modulation*, neboli *PWM*. -Z MicroPythonu jde tahle funkce ovládat pomocí třídy -`machine.PWM`. -Každý objekt téhle třídy umí ovládat jednu nožičku -a dají se u něj nastavit dva parametry: - -* `freq` – frekvence, tedy kolikrát za sekundu se LED rozsvítí a zase zhasne a -* `duty` – anglicky *duty cycle*, česky *střída*, nastavuje „šířku pulzu”, - tedy jak dlouho bude dioda při každém bliknutí svítit. - Hodnota `duty` může být od 0, kdy LED - nesvítí vůbec, do 1023, kdy svítí celou dobu. - Nastavíš-li `duty=512`, bude dioda - svítit s poloviční intenzitou (512 = 1024/2). - -Nastavíš-li `PWM(freq=50, duty=512)`, dioda bude blikat 50× za sekundu. -Vždycky jednu setinu vteřiny bude svítit a na jednu -setinu vteřiny zhasne. - -```python -from machine import Pin, PWM -from time import sleep - -pin_diody = Pin(14, Pin.OUT) -pwm = PWM(pin_diody, freq=50, duty=512) -``` - -Zkus nastavit i nižší frekvenci, třeba 3 nebo 1, ať blikání vidíš přímo! - -PWM se dá zrušit metodou `pwm.deinit()`. -Jako s otvíráním souborů, je dobré po sobě uklidit – -i když zatím můžeš jednoduše restartovat celé zařízení. - - -## Tóny a melodie - -Vezmi si další součástku – piezobudič, neboli „bzučítko”. - -Tahle malá věc obsahuje speciální materiál, který se, -když ho připojíš ke zdroji napětí, trošku roztáhne. -Roztažením zatlačí na okolní vzduch a vytvoří tlakovou -vlnu, která může doputovat až k tvým uším. - -Zkus si to – když bzučítko připojíš na `3V` -a `G` (tentokrát je jedno kterým směrem), uslyšíš tiché lupnutí. -A podobné lupnutí uslyšíš když součástku zase odpojíš. - -Co se stane, když budeš napětí připojovat a odpojovat, řekněme, 32× za vteřinu? - -Nebo 65×? - -Nebo některou z těchto frekvencí? -Hz – [Hertz](https://en.wikipedia.org/wiki/Hertz) – je jednotka frekvence; -„49 Hz“ znamená „49× za sekundu“. - -| Nota | Frekvence | -|:-----|----------:| -| C1 | 32,70 Hz | -| D | 36,71 Hz | -| E | 41,20 Hz | -| F | 43,65 Hz | -| G | 49,00 Hz | -| A | 55,00 Hz | -| H | 61,74 Hz | -| C2 | 65,41 Hz | - -Naprogramuj písničku! Potřebuješ-li víc not, pusť si [program](static/noty.py), -který vypočítá další frekvence. - - -## Další ovládání - -Teď si vezmi dvě tlačítka a připoj je k modulu: -`GND` vždycky na `G`, `VCC` vždycky na `3V` a -`OUT` u jednoho tlačítka na `D1` a u druhého na `D2`. - -Tlačítko funguje tak, že OUT spojí buď s VCC (5V) -nebo GND, podle toho, jestli je tlačítko stisknuté. -(A navíc to taky teda svítí, ale to je teď vedlejší.) - -Zkus si, jestli se zvládneš MicroPythonu zeptat, jestli je tlačítko zapnuté. -Mělo by to být podobné jako u příkladu s tlačítkem FLASH. - -Zvládneš napsat program, který bude bzučet bzučítkem -a přitom se jedním tlačítkem bude dát zvyšovat tón a druhým snižovat? - -Program si (na svém počítači) ulož, ať se k němu můžeš vrátit. - - -## Rotace - -Čas na další součástku! Tentokrát to bude *servomotor*. - -Servomotor je součástka, která má v sobě zabudovaný -ovladač, se kterým si naše zařízení může povídat -jednoduchým „elektronickým jazykem” – *protokolem*. -Motorku můžeš posílat impulzy a podle délky impulzu -se servomotor natočí. -Při krátkých impulzech se natočí víc na jednu stranu, -při dlouhých na druhou. -Impulzy musíš posílat neustále, jinak se servomotor -vypne. - -Na rozdíl od bzučítka, kde o výšce tónu rozhodovala -frekvence (`freq`) – kolikrát za vteřinu -se ozve lupnutí – a LED, kde o intenzitě rozhodovala -střída (`duty`) – poměr mezi dobou kdy -dioda svítí a kdy nesvítí, u servomotoru rozhoduje -tzv. *šířka pulzu*: jak dlouho se napětí udrží -na 3,3 V, než se přepne zpátky na 0 V. - - - -V praxi to znamená, že můžeš nastavit `freq` -na 50 Hz, a `duty` měnit cca od 35 -(úplně vlevo) přes 77 (uprostřed) po 120 (úplně vpravo). - -Dost ale teorie, pojďme si to vyzkoušet! Napřed musíš motorek zapojit: - -* hnědý drát (zem) na `G`, -* červený drát (napájení) na `3V` a -* oranžový drát (data) na `D4`. - -Nožička `D4` odpovídá `Pin(2)`, takže kód k otáčení motorku je: - -```python -from machine import Pin, PWM -from time import sleep - -pin_motorku = Pin(2, Pin.OUT) -pwm = PWM(pin_motorku, freq=50, duty=77) -pwm.duty(35) -``` - -Zkus motorkem otáčet nastavováním `duty` na 35 do 120. -Kdyby se náhodou stalo, že se modul restartuje a -konzole přestane fungovat, zkus ho odpojit a znovu -připojit. Kdyby to nepomohlo, motorek ti dneska -nebude fungovat. Za chvíli si řekneme proč; zatím (jsi-li na kurzu) -se přidej do dvojice k někomu, komu to funguje. - - -## Poznámka o napájení a napětí - -K tomu, aby se otočil motor, je potřeba mnohem víc -energie, než k rozsvícení světýlka. -Z USB z počítače té energie dostaneš docela málo, -proto můžou být s motorkem problémy. - -Jak to řešit, až si přestaneš hrát a budeš chtít motorkem otáčet „doopravdy”? - -Elektronika, která je na našem modulu mimo -malou destičku s „mozkem” má dva hlavní úkoly: - -* Převádět *komunikaci* z USB, která je - celkem složitě zakódovaná, na něco čemu - malé zařízení rozumí - (konkrétně protokol [UART](https://en.wikipedia.org/wiki/UART) přes nožičky `TX` a `RX`). -* Převádět napětí 5 V, které poskytuje USB, - na 3,3 V které potřebuje modul. - -Když energie z USB přestane stačit, dá se koupit -zařízení, které zvládne převádět komunikaci -a napájení vyřešit z jiného zdroje 5 V. -Kdybys to někdy zkoušela, příslušné zařízení -koupíš pod názvem *USB-TTL adapter* a vypadá -nejčastěji takhle: - -{{ figure( - img=static("usb-ttl.png"), - alt="USB-TTL adapter", -) }} - -K modulu pak tento převodník a zdroj napětí připojíš takto: - - -* `GND` na převodníku – `G` na desce -* `RX` na převodníku – `TX` (!) na desce -* `TX` na převodníku – `RX` (!) na desce -* `+5V` na zdroji napětí na `VIN` na desce -* Zem na zdroji napětí na `G` na desce - -Pozor, 5V nepřipojuj jinam než na `VIN`! - - -## Napětí -Další důvod, proč ti servomotor někdy nefunguje dobře, -je to, že je stavěný na 5 voltů, ne na 3,3 které -poskytuje modul. - -Když připojíš zařízení k menšímu napětí, než -potřebuje, většinou buď nebude fungovat, nebo bude -dělat „míň” než by mělo: LED bude míň svítit, -reproduktor bude tišší, motorek se bude točit pomaleji nebo s menší silou. - -Když naopak připojíš zařízení k *většímu* napětí, -než na jaké je stavěno, nejspíš ho nadobro zničíš. -Když připojíš červenou LED přímo na 3,3 V, přestane fungovat; -když připojíš malý servomotorek na zdroj 24 V, může začít hořet. -A ačkoli lidem malá napětí jako 5 V nevadí, -když připojíš do zásuvky s 230 V {{ gnd('sám', 'sama') }} sebe, můžeš umřít. -Takže na velká napětí pozor! - -My motorek připojujeme na malé napětí a zmenšený -výkon nám příliš nevadí – dokud se to -otáčí, víc rychlosti ani síly nepotřebujeme. - -Až to ale potřebovat budeš – například až budeš -servomotorkem pohánět ruku robota, která bude zvedat -těžké náklady, budeš potřebovat dvě věci: - -* Externí napájení – jako předtím bude motorek potřebovat zvláštní zdroj 5V. -* Na datový signál je potřeba použít *převodník úrovní* - (angl. *logic level converter*), - který převede třívoltový signál na pětivoltový. - -Kdybys to někdy potřebovala, ozvi se koučům – i po -workshopu ti určitě rádi poradí nebo ti aspoň řeknou -koho se zeptat! - -{# -## Ukládání souboru - -XXX: Tahle kapitola ještě bohužel není dopsaná. -#} - -## Barevná světýlka - -Tak, dost teorie; vezmi si novou hračku! -Tentokrát to bude LED pásek. - -Na pásku máš 8 malých čtverečků. -Každý z nich obsahuje docela hodně elektroniky: -tři barevné LED (červenou, zelenou a modrou) -a čip, který je umí ovládat pomocí informací, -které dostane přes jediný drátek z modulu. - -Takové pásky se prodávají po metrech a dají se -nastříhat – mezi jednotlivými světýlky si všimni čárky, -která naznačuje, kde máš střihnout. -Energie z USB stačí zhruba na osm světýlek, proto jsi jich dostal{{a}} tolik. - -Tenhle LED pásek je, podobně jako servomotorek, stavěný -na pět voltů. Na rozdíl od motorku, který se s 3,3 V -trochu roztočil, se ale s nižším napětím ani nerozsvítí. -Naštěstí ale potřebuje 5 V jen na napájení; -řídící signál s informacemi o barvičkách může mít 3,3 V. - -Pojďme pásek zapojit: - -* `GND` pásku (bílý drátek) připoj na `G` -* `DI` (*data in* – zelený drátek) připoj na `D4` -* `+5V` (červený drátek) připoj: - * na `VU`, má-li tvoje destička tuhle nožičku, - * jinak na `VIN`. - -Nožička `VU`/`VIN` poskytuje 5 voltů. -Pozor na ni: nepřipojuj na ni zařízení, které se s pěti volty nevyrovnají. - -Máš-li zapojeno, můžeš začít programovat. -„Jazyk”, kterým „mluví” tenhle LED pásek je trošku -složitější než signál PWM, ale MicroPython obsahuje -speciální knihovnu, která s páskem komunikovat umí. -Vypadá to nějak takhle: - - -
-from machine import Pin
-from neopixel import NeoPixel
-
-POCET_LED = 8
-pin = Pin(2, Pin.OUT)
-np = NeoPixel(pin, POCET_LED)
-np[0] = (255, 255, 255)
-np.write()
-
- - -Co znamenají ta čísla (`0` a `255`), na to už jistě přijdeš sama. -Jen při experimentování nezapomeň zavolat -`np.write()`, tím se informace pošlou do LED pásku. - -Zvládneš naprogramovat semafor? - - -{# -## Wi-fi -(XXX) - -## Kam dál? -(XXX) -#} diff --git a/lessons/beginners/micropython/info.yml b/lessons/beginners/micropython/info.yml deleted file mode 100644 index 903f9f3e..00000000 --- a/lessons/beginners/micropython/info.yml +++ /dev/null @@ -1,26 +0,0 @@ -title: MicroPython -style: md -attribution: -- Pro PyLadies Brno napsal Petr Viktorin, 2016-2017. -- Diagramy s LED vytvořeny pomocí [Fritzing](http://fritzing.org). -license: cc-by-sa-40 -css: | - .part-green { outline: 2px solid hsla(113, 100%, 50%, 1); - background-color: hsla(113, 100%, 50%, 0.25); } - .part-cyan { outline: 2px solid hsla(180, 100%, 50%, 1); - background-color: hsla(180, 100%, 50%, 0.25); } - .part-blue { outline: 2px solid hsla(236, 100%, 50%, 1); - background-color: hsla(236, 100%, 50%, 0.25); } - .part-yellow { outline: 2px solid hsla( 60, 100%, 50%, 1); - background-color: hsla( 60, 100%, 50%, 0.25); } - .part-orange { outline: 2px solid hsla( 42, 100%, 50%, 1); - background-color: hsla( 42, 100%, 50%, 0.25); } - .part-red { outline: 2px solid hsla( 0, 100%, 50%, 1); - background-color: hsla( 0, 100%, 50%, 0.25); } - .pull-right, .pull-left { margin: 1em; } - .highlight { background-color: hsla( 0, 100%, 50%, 0.1); } - .img-fluid { max-width: 100%; } - .highlight-nocolor{ background-color: hsla( 60, 100%, 50%, 0.75); } - .highlight-red { background-color: hsla( 0, 100%, 50%, 0.25); } - .highlight-green { background-color: hsla(113, 100%, 50%, 0.25); } - .highlight-blue { background-color: hsla(236, 100%, 50%, 0.25); } diff --git a/lessons/beginners/modules/index.md b/lessons/beginners/modules/index.md deleted file mode 100644 index 3ae35a51..00000000 --- a/lessons/beginners/modules/index.md +++ /dev/null @@ -1,142 +0,0 @@ -# Moduly - -Modul je v Pythonu něco, z čeho můžeš importovat. -Třeba z modulu `math` můžeš importovat funkci `sqrt`: - -```python -from math import sqrt - -print(sqrt(2)) -``` - -Kromě importování jednotlivých proměnných z modulu -můžeš importovat i celý modul najednou. -K tomu, co modul nabízí, se pak dostaneš pomocí -tečky – podobně jako se pomocí `'Ahoj'.upper` dostaneš k metodě, kterou nabízí -řetězec. - -Například: - -```python -import turtle - -turtle.left(90) -turtle.color('red') -turtle.forward(100) -turtle.exitonclick() -``` - -```python -import math - -print(math.cos(math.pi)) -``` - -> [note] Hvězdičky nechceme -> -> Možná jsi v dokumentaci nebo na jiném kurzu viděl{{a}} příkaz import -> s hvězdičkou (`*`). -> Pokud ano, v rámci tohoto kurzu na hvězdičku prosím -> zapomeň a importuj místo toho radši celý modul. -> Až začneš psát větší programy, zjednoduší ti -> to práci. - - -## Vlastní moduly - -A teď to hlavní! -Můžeš vytvořit vlastní importovatelný modul -a to jen tak, že uděláš pythonní soubor. -Funkce, které v něm nadefinuješ, a globální proměnné, -které v něm nastavíš, pak budou k dispozici tam, kde modul naimportuješ. - -Zkus si to! -Vytvoř soubor `louka.py` a do něj napiš: - -```python -barva_travy = 'zelená' -pocet_kotatek = 28 - -def popis_stav(): - return f'Tráva je {barva_travy}. Prohání se po ní {pocet_kotatek} koťátek' -``` - -A pak do dalšího souboru, třeba `vypis.py`, napiš: - -```python -import louka - -print(louka.popis_stav()) -``` - -A pak spusť `vypis.py`: - -```console -$ python vypis.py -``` - -Příkaz `import` hledá soubory (mimo jiné) v adresáři, -ve kterém je „hlavní modul” programu – tedy soubor -který spouštíš (tady `vypis.py`). -Oba soubory by proto měly být ve stejném adresáři. - - -## Vedlejší efekty - -Co přesně dělá příkaz `import louka`? - -Python najde příslušný soubor (`louka.py`) a provede v něm všechny příkazy, -odshora dolů, jako v normálním Pythonním programu. -Všechny globální proměnné (včetně nadefinovaných funkcí) pak dá k dispozici -kódu, který importoval. - -Když pak stejný modul importuješ podruhé, už se neprovádí všechno znovu. -Druhý import jen zpřístupní stejnou sadu proměnných/funkcí jako ten první. - -Zkus si to – na konci `louka.py` dopiš: - -```python -print('Louka je zelená!') -``` - -Spusť `python` (máš-li ho už spuštěný, ukonči a spusť znovu) -a zadej v něm: - -```pycon ->>> print('První import:') ->>> import louka ->>> print('Druhý import:') ->>> import louka -``` - -Výpis se objeví jen poprvé. -Co víc, když teď soubor `louka.py` změníš, změny se do naimportovaného modulu -nepromítnou. -Aby se promítly, musíš Python zavřít a spustit znovu. -(I proto je dobré pouštět programy ze souborů – při každém spuštění se -znovu načte aktuální verze modulů.) - -Ale zpátky k volání `print`. -Přijde ti trochu divné, že příkaz `import louka` něco vypíše na obrazovku? - -Když takhle modul při importu „dělá“ něco víc než jen nastavení proměnných -a funkcí, říká se, že má *vedlejší efekt* (angl. *side effect*). -Vedlejší efekt může být vypsání něčeho na obrazovku nebo do souboru, -vykreslení okýnka na obrazovku, otázka na uživatele pomocí `input`, atp. - -V modulech připravených na importování se vedlejším efektům vyhýbej: -úloha takového modulu je dát k dispozici *funkce*, které něco dělají, -ne to udělat přímo. -Všimni si například, že `import turtle` neukáže okýnko – to se objeví až po -zavolání `turtle.forward()`. -Importem si programátor připravuje nástroje; teprve zavoláním funkce je používá. - -Příkaz `print` proto radši z modulu zase smaž. - - -## Adresář pro každý projekt - -Od teď budeš občas psát větší projekty, -které budou obsahovat více souvisejících souborů. -Pro každý takový projekt si udělej zvláštní adresář. -Lépe se pak vyznáš v tom, ke kterému projektu který soubor patří. diff --git a/lessons/beginners/modules/info.yml b/lessons/beginners/modules/info.yml deleted file mode 100644 index c6958401..00000000 --- a/lessons/beginners/modules/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Moduly -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/nested-list/index.md b/lessons/beginners/nested-list/index.md deleted file mode 100644 index 6df98017..00000000 --- a/lessons/beginners/nested-list/index.md +++ /dev/null @@ -1,84 +0,0 @@ -# Vnořené seznamy - -Seznam může obsahovat jakýkoli typ hodnot: čísla, řetězce a tak dále: - -```python -prvocisla = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41] -zviratka = ['pes', 'kočka', 'králík'] -odpovedi = [False, False, False, True, False, True, True] -``` - -Stejně tak může seznam obsahovat i další seznamy. -Podobně jako seznam čísel nebo seznam řetězců tak lze vytvořit seznam seznamů: - -```python -seznam_seznamu = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] -``` - -Takový seznam se chová docela normálně – můžeš z něj třeba brát jednotlivé -prvky (které jsou ovšem taky seznamy): - -```python -prvni_seznam = seznam_seznamu[0] -print(prvni_seznam) -``` - -A protože jsou prvky samy seznamy, -dá se mluvit o věcech jako „první prvek druhého seznamu”: - -```python -druhy_seznam = seznam_seznamu[1] -prvni_prvek_druheho_seznamu = druhy_seznam[0] -print(prvni_prvek_druheho_seznamu) -``` - -A protože výraz `seznam_seznamu[1]` -označuje seznam, můžeš brát prvky přímo z něj: - -```python -prvni_prvek_druheho_seznamu = (seznam_seznamu[1])[0] -``` - -Neboli: - -```python -prvni_prvek_druheho_seznamu = seznam_seznamu[1][0] -``` - -A má tahle věc nějaké použití? -Stejně jako vnořené cykly `for` ti umožnily vypsat tabulku, vnořené seznamy -ti umožní si tabulku „zapamatovat”. - -```python -def vytvor_radek_tabulky(cislo_radku, velikost): - """Vrátí seznam – daný řádek tabulky s násobilkou""" - radek = [] - for cislo_sloupce in range(velikost): - radek.append(cislo_radku * cislo_sloupce) - return radek - -def vytvor_tabulku(velikost): - """Vrátí seznam seznamů – tabulku s násobilkou""" - seznam_radku = [] - for cislo_radku in range(velikost): - radek = vytvor_radek_tabulky(cislo_radku, velikost) - seznam_radku.append(radek) - return seznam_radku - -nasobilka = vytvor_tabulku(11) - -print(nasobilka[2][3]) # dva krát tři -print(nasobilka[5][2]) # pět krát dva -print(nasobilka[8][7]) # osm krát sedm - -# Vypsání celé tabulky -for radek in nasobilka: - for cislo in radek: - print(cislo, end=' ') - print() -``` - -Co s takovou „zapamatovanou” tabulkou? -Můžeš si do ní uložit třeba pozice -figurek na šachovnici nebo křížků a koleček -ve *2D* piškvorkách. diff --git a/lessons/beginners/nested-list/info.yml b/lessons/beginners/nested-list/info.yml deleted file mode 100644 index 7167651c..00000000 --- a/lessons/beginners/nested-list/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Vnořené seznamy -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2020. -license: cc-by-sa-40 diff --git a/lessons/beginners/nested-traceback/index.md b/lessons/beginners/nested-traceback/index.md deleted file mode 100644 index 88487829..00000000 --- a/lessons/beginners/nested-traceback/index.md +++ /dev/null @@ -1,71 +0,0 @@ -# Výpisy chyb ze zanořených funkcí - -Teď si ukážeme (nebo zopakujeme), jak Python vypíše chybu, která -nastane v zanořené funkci. - -Pročti si následující ukázkový příklad. - -> [note] -> Tohle je absurdní ilustrační příklad, ne ukázka jak dobře programovat! - - - -```python -def podel(delenec, delitel): - """Podělí čísla mezi sebou a vrátí výsledek.""" - return delenec / delitel # řádek 3 - - -def podel_nulou(cislo): - """Vydělí dané číslo nulou a vrátí výsledek.""" - return podel(cislo, 0) # řádek 8 - - -def ukaz_priklad(): - """Spočítá ukázkový příklad a výsledek vypíše (nevrátí!)""" - vysledek = podel_nulou(3) # řádek 13 - print(vysledek) - -ukaz_priklad() # řádek 16 -``` - -Co se stane, když tohle pustíš? - -```pycon -Traceback (most recent call last): - File "ukazka.py", line 16, in - ukaz_priklad() - File "ukazka.py", line 13, in ukaz_priklad - vysledek = podel_nulou(3) - File "ukazka.py", line 8, in podel_nulou - return podel(cislo, 0) - File "ukazka.py", line 3, in podel - return delenec / delitel -ZeroDivisionError: division by zero -``` - -Všimni si, že každá z funkcí, jejíž volání vedlo k chybě, je uvedena ve výpisu. -Skutečná chyba (tedy místo, které musíš opravit) je asi někde poblíž těchto -míst: - -- Na řádku 3, ve funkci `podel`, vzniklo dělení nulou. - Měla by funkce `podel` zjistit, jestli dostane nulu, a nějak na to - zareagovat? -- Na řádku 8, ve funkci `podel_nulou`, kde voláš `podel` s dělitelem 0. - Má to tak opravdu být? -- Na řádku 13, ve funkci `ukaz_priklad`, kde voláš funkci `podel_nulou`. - Kdybys to nedělal{{a}}, chyba by taky nevznikla! -- Na řádku 16, ne ve funkci, když voláš funkci `ukaz_priklad`. - Možná by to chtělo použít jiný příklad? - -Python (a asi ani ty) nemůže vědět, co tím programem programátor myslel, -a kdy by tedy bylo nejlepší chybu opravit. -Ukáže tedy v programu všechna místa, která k chybě vedla. -Je na tobě, abys z nich vybral{{a}} to nejvhodnější a zaměřil{{a}} se na něj. - -Tahle ukázka je samozřejmě jen teoretická, ale v reálných programech vypadá -hlášení chyb stejně. -Až takovou složitou hlášku uvidíš, nepanikař! -Python se snaží co nejvíc napovědět a usnadnit ti chybu najít a opravit. -Sice stroze a anglicky, ale snaží. -Vyjdi mu vstříc a nauč se tyhle hlášky číst. diff --git a/lessons/beginners/nested-traceback/info.yml b/lessons/beginners/nested-traceback/info.yml deleted file mode 100644 index 9828ce46..00000000 --- a/lessons/beginners/nested-traceback/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Chybové hlášky ze zanořených funkcí -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/prefer-return/index.md b/lessons/beginners/prefer-return/index.md deleted file mode 100644 index 10da51b6..00000000 --- a/lessons/beginners/prefer-return/index.md +++ /dev/null @@ -1,92 +0,0 @@ -# Vrátit nebo vypsat? - -Podívejme se teď na následující program, který vypíše obsah elipsy: - -```python -from math import pi - -def obsah_elipsy(a, b): - return pi * a * b - -print('Obsah elipsy s poloosami 3 a 5 je', obsah_elipsy(3, 5), 'cm2') -``` - -Takový program se teoreticky dá napsat i s procedurou, tedy funkcí, která nic -nevrací. -Procedura může výsledek třeba vypsat na obrazovku: - -```python -from math import pi - -def obsah_elipsy(a, b): - print('Obsah je', pi * a * b) # Pozor, `print` místo `return`! - -obsah_elipsy(3, 5) -``` - -Program takhle funguje, ale přichází o jednu z hlavních výhod funkcí: -možnost vrácenou hodnotu použít i jinak než jen v `print`. - -Funkci, která *vrací* výsledek, můžeš použít v dalších výpočtech: - -```python -def objem_eliptickeho_valce(a, b, vyska): - return obsah_elipsy(a, b) * vyska - -print(objem_eliptickeho_valce(3, 5, 3)) -``` - -... ale s procedurou, která výsledek přímo vypíše, by to nešlo. -Proto je dobré psát funkce, které spočítané hodnoty vrací, -a zpracování výsledku (např. vypsání) nechat na kód mimo funkci. - -Další důvod, proč hodnoty spíš vracet než vypisovat, je ten, že jedna funkce se -dá použít v různých situacích. -Proceduru s `print` by nešlo rozumně použít tehdy, když nás příkazová -řádka vůbec nezajímá – třeba v grafické hře, webové aplikaci nebo pro ovládání -robota. - -Podobně je to se vstupem: když použiju v rámci své funkce `input`, bude se -moje funkce dát použít jen v situacích, kdy je u počítače klávesnice a za ní -člověk. -Proto je lepší funkcím potřebné informace předávat jako argumenty -a volání `input` (nebo čtení textového políčka či měření čidlem robota) -nemít ve funkci, ale vně, v kódu, který funkci volá: - -```python -from math import pi - -def obsah_elipsy(a, b): - """Vrátí obsah elipsy s poloosami daných délek""" - # Jen samotný výpočet: - return pi * a * b - -# print a input jsou "venku": -x = float(input('Zadej délku poloosy 1: ')) -y = float(input('Zadej délku poloosy 2: ')) -print('Obsah je', obsah_elipsy(x, y)) -``` - -Samozřejmě existují výjimky: procedura, která přímo vytváří textový výpis -(např. tabulku), může používat `print`; funkce, která načítá textové informace -(jako `ano_nebo_ne` výše), zase `input`. -Když ale funkce něco *počítá*, nebo když si nejsi jist{{gnd('ý', 'á')}}, -je dobré ve funkci `print` ani `input` nemít. - - -## None - -Když funkce neskončí příkazem `return`, -automaticky se vrátí hodnota `None`. - -Je to hodnota zabudovaná přímo do Pythonu, podobně jako `True` nebo `False`, -a znamená „nic“. - -```python -def nic(): - """Tahle funkce nic nedělá """ - -print(nic()) -``` - -Procedury v Pythonu vracejí právě toto „nic“. diff --git a/lessons/beginners/prefer-return/info.yml b/lessons/beginners/prefer-return/info.yml deleted file mode 100644 index 453e0e39..00000000 --- a/lessons/beginners/prefer-return/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Vrátit nebo vypsat? -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 \ No newline at end of file diff --git a/lessons/beginners/print/index.md b/lessons/beginners/print/index.md deleted file mode 100644 index 4a498356..00000000 --- a/lessons/beginners/print/index.md +++ /dev/null @@ -1,154 +0,0 @@ -# Print a chybové hlášky - -Vytvoř v editoru nový soubor, ulož ho do adresáře pro dnešní lekci -pod jménem `printing.py` a napiš do něj teď už známý příkaz: - -```python -print("Ahoj světe!") -``` - -Program spusť: -* pokud ti už na začátku příkazové řádky nesvítí `(venv)`, - aktivuj si virtuální prostředí, -* pomocí `cd` donaviguj do adresáře s programem, -* zadej `python printing.py`. - -Funguje? Doufám, že ano; za chvíli ho vylepšíme. - - -## Další příkazy - -Zkus do programu postupně, po jednom, přidávat další řádky. -Po přidání každého dalšího `print` program znovu spusť a vyzkoušej, jestli -funguje. - -Abys nemusel{{a}} v příkazové řádce stále dokola psát `python printing.py`, -zkus zmáčknout na klávesnici šipku nahoru, . -Vrátíš se tak k předchozímu příkazu, který stačí „odklepnout“ pomocí -Enter. - - -Úplně každý příkaz ti asi nebude fungovat hned napoprvé – kdyby program -dělal něco divného, přeskoč na další sekci, *jak číst chyby*. - -```python -print(1) -print(1, 2, 3) -print(1 + 1) -print(3 * 8) -print(10 - 2.2) -print(3 + (4 + 6) * 8 / 2 - 1) -print('*' * 80) -print("Ahoj" + " " + "PyLadies!") -print("Součet čísel 3 a 8 je", 3 + 8) -print('Máma má mísu') -print(V míse je maso.) -``` - -> [note] Řetězce -> Proč jsou některé hodnoty v uvozovkách a některé ne? -> Pokud chceš v Pythonu pracovat s textem, musíš ho obalit do uvozovek, aby Python -> věděl, že se k němu má chovat jinak než například k číslům. -> Více se dozvíš později, zatím si zapamatuj, že se takovýto text označuje označuje v programovací -> hantýrce jako `řetězec`. - -## Jak číst chyby - -Často zjistíš, že program, který napíšeš, nebude fungovat hned napoprvé. -Počítač je hloupý stroj; pokud instrukce nenapíšeš přesně podle pravidel jazyka -Python, neumí si domyslet, co po něm chceš. -Ale nevěš hlavu, stává se to všem programátorům. -Důležité je vědět, jak chybu najít. -A k tomu ti pomůžou chybové výpisy. - -Pokud program výše opíšeš přesně, vypíše po spuštění následující hlášku: - -
-  File "~/pyladies/02/printing.py", line 11
-    print(V míse je maso.)
-               ^
-SyntaxError: invalid syntax
-
- -Při chybě Python napřed zmíní jméno souboru a -číslo řádku, na kterém si chyby všimnul. -Potom vypíše celý řádek s chybou -a nakonec oznámí druh chyby -(v tomto případě je to „syntaktická chyba“) -a případně nějaké bližší upřesnění. - -> [note] Pro zvídavé -> Jak se od téhle chyby liší ta, která nastane, když zkusíš sečíst číslo a řetězec? -> Nebo když zkusíš dělit nulou? - -Chybové hlášky můžou být ze začátku těžko pochopitelné, -zvyknout se na ně dá asi jenom praxí. -Pro tebe bude ze začátku důležité hlavně ono číslo řádku. -Když víš, že chyba je na řádku 11, -můžeš se podívat na tento řádek a zkusit chybu najít. - -Když chyba není na daném řádku, může být ještě -o pár řádků výš nebo níž: -Python občas nesdílí lidské představy o tom, kde přesně chyba *je*. -Ukáže jen, kde si jí sám *všimnul*. - -V našem případě je chyba v tom, že kolem řetězce *V míse je maso* nejsou uvozovky. -Přidej je a program znovu spusť. -Jestli funguje, gratuluji! -Jinak chybu opět oprav a opakuj, dokud to nebude fungovat :) - - -## Jak funguje program - -Teď, když program běží, se můžeme podívat, co se při -jeho spuštění vlastně děje. -Je to zatím docela jednoduché: příkazy se provádějí jeden po druhém, -odshora dolů. -Program je jako recept na vaření: seznam instrukcí, které říkají co je potřeba -udělat. - -Zanedlouho budou tvoje programy připomínat spíš recepty na -čarodějné lektvary (*počkej do úplňku a pokud je Mars -v konjunkci s Jupiterem, třikrát zamíchej*), -ale základní myšlenka je stále stejná: -počítač „čte“ odshora dolů a provádí příkazy jeden po druhém. - - -## Print a výrazy - -A z jakých že instrukcí se náš „recept“ skládá? - -Ten `print`, který tu celou dobu používáš, je *funkce*. -O funkcích se ještě budeme bavit později, -teď stačí vědět, že když napíšeš `print` -a za to do závorky několik *výrazů* (angl. *expressions*) -oddělených čárkou, hodnoty těchto výrazů se vypíšou. - -A co že je ten výraz? -V našem programu máš několik příkladů: -výraz je číslo, řetězec nebo nějaká (třeba matematická) operace -složená z více výrazů. -Třeba výraz `3 + 8` sčítá výrazy `3` a `8`. - -V sekci o [proměnných]({{ lesson_url('beginners/variables') }}) se -na výrazy a jejich hodnoty podíváme podrobněji. - -> [style-note] Typografická vsuvka -> Všimni si stylu zápisu: jako v češtině se po otevírací závorce a před -> uzavírací závorkou nepíše mezera; na rozdíl od češtiny ale mezera není -> mezi `print` a závorkou. -> ```python -> print("Ahoj!") -> ``` -> -> S čárkou je to jako v češtině: mezeru píšeme po čárce, ale ne před ní: -> ```python -> print(1, 2, 3) -> ``` -> -> Kolem operátorů jako `+` a `/` se obyčejně píše jedna mezera zleva a -> jedna zprava. Někdy je ale přehlednější obě vynechat: -> ```python -> print(2 + 8) -> print("Jedna a půl je", 1 + 1/2) -> ``` diff --git a/lessons/beginners/print/info.yml b/lessons/beginners/print/info.yml deleted file mode 100644 index c353cbd5..00000000 --- a/lessons/beginners/print/info.yml +++ /dev/null @@ -1,13 +0,0 @@ -title: Print a chybové hlášky -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 -css: | - .err-lineno { - display: inline-block; - background-color: #FCC; - } - .err-exctype { - display: inline-block; - background-color: #CFC; - } diff --git a/lessons/beginners/range/index.md b/lessons/beginners/range/index.md deleted file mode 100644 index c3e4617f..00000000 --- a/lessons/beginners/range/index.md +++ /dev/null @@ -1,131 +0,0 @@ -# Range – sekvence čísel - -Funkce `range(10)` vrátí speciální hodnotu, -která v sobě obsahuje čísla od 0 do 9: - -```pycon ->>> sekvence = range(10) ->>> sekvence -range(0, 10) -``` - -Je to hodnota typu `range`, podobně jako čísla jsou typu `int`, řetězce typu -`str`, nebo seznamy typu `list`. - -Chceš-li se podívat, co v tomhle `range(0, 10)` vlastně je, máš dvě základní -možnosti – projít ho cyklem `for` nebo převést na seznam konkrétních čísel: - -```pycon ->>> sekvence -range(0, 10) ->>> for i in sekvence: -... print(i) -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 ->>> list(sekvence) -[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] -``` - -Možná se ptáš – proč tak složitě? -Proč se místo `range(0, 10)` prostě ta čísla neukážou rovnou? - -Je to proto, že `range` se dá použít na opravdu dlouhé řady čísel: - -```pycon ->>> range(10000) -range(0, 10000) ->>> list(range(10000)) -[0, 1, 2, 3, ..., 9999] -``` - -Kdybys zkusil{{a}} třeba `list(range(1000000000000000))`, počítači -dojde paměť. -Biliarda čísel se tam prostě nevejde. -Python vyhodí výjimku `MemoryError`. - - -> [warning] -> Pokud máš na počítači v jiném okně neuloženou práci, radši `list(range(...))` -> s hodně vysokými čísly nezkoušej. -> -> U absurdně vysokého čísla jako `1000000000000000` Python předem ví, -> že mu paměť dojde, a tak ohlásí chybu ještě než se bude snažit seznam vytvořit. -> U trochu menšího čísla (např. `1000000000`, ale na každém počítači je to -> jinak) se může stát, že se Python pokusí seznam začít tvořit, zaplní přitom -> většinu dostupné paměti a počítač „zamrzne“. -> V závislosti na systému se pak třeba může stát, že reakce na -> Ctrl+C bude trvat hodně dlouho. - -Se samotným `range(1000000000000000)` ale není problém. -S konceptem *všech čísel od 0 do biliardy* se počítač vypořádá, i když si je -neumí „zapamatovat“ všechny *najednou*. - -Je spousta věcí, které Python umí s `range` udělat, aniž by potřeboval -„spočítat“ každé z čísel. -Spousta operací, které znáš od seznamů, bude fungovat i s `range`: - -```pycon ->>> zajimava_cisla = range(8, 10000, 3) # Každé třetí číslo od 8 do 9999 ->>> zajimava_cisla[80] # Osmdesáté "zajímavé číslo" -248 ->>> zajimava_cisla[:5] # Prvních 5 "zajímavých čísel" -range(8, 23, 3) ->>> list(zajimava_cisla[:5]) # Vypsání prvních 5 "zajímavých čísel" -[8, 11, 14, 17, 20] ->>> len(zajimava_cisla) # Kolik tam je čísel? -3331 ->>> 1337 in zajimava_cisla # Je v této sekvenci moje konkrétní číslo ? -True ->>> zajimava_cisla.index(1337) # Kolikáté je? -443 -``` - -```pycon ->>> import random ->>> random.choice(zajimava_cisla) -1229 -``` - -```pycon ->>> for i in zajimava_cisla: -... print(i) -... if i > 20: -... break # Stačilo! -8 -11 -14 -17 -20 -23 -``` - -Objekt `range` ale nejde měnit – mazání prvků nebo metody jako -`zajimava_cisla.sort()`, `zajimava_cisla.pop()` fungovat nebudou. - -> [note] Proč ne? -> Když máš objekt jako `range(8, 10000, 3)`, osmdesátý prvek je jen trocha -> matematiky: spočítáš `8 + 3 * 80` a zkontroluješ že to nepřesáhlo `10000`. -> Podobně je to s ostatními sekvencemi „všech X-tých čísel od -> A do B“, tedy s ostatními `range`. -> -> Kdyby ale šlo udělat něco jako: -> -> ```python -> sekvence = range(8, 10000, 3) -> del sekvence[10] -> sekvence.insert(103, 'ježek') -> ``` -> -> … jde najednou o mnohem složitější koncept, kde se N-tý prvek hledá mnohem -> hůř. Už to není jednoduchá sekvence čísel – už to není `range`, ale spíš -> seznam jakýchkoli hodnot. - -Pokud budeš něco, co `range` neumí, potřebovat, převeď `range` na seznam. diff --git a/lessons/beginners/range/info.yml b/lessons/beginners/range/info.yml deleted file mode 100644 index 10fbe5cc..00000000 --- a/lessons/beginners/range/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Range - sekvence čísel -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2019. -license: cc-by-sa-40 diff --git a/lessons/beginners/reassignment/index.md b/lessons/beginners/reassignment/index.md deleted file mode 100644 index 8032da0e..00000000 --- a/lessons/beginners/reassignment/index.md +++ /dev/null @@ -1,121 +0,0 @@ -# Přepisování proměnných - -Už víš, že hodnota proměnné se může v čase měnit: -když přiřadíš do už existující proměnné, stará hodnota se zahodí -a použije se nová. - -```python -oblibena_barva = 'modrá' -print(oblibena_barva) - -oblibena_barva = 'žlutá' -print(oblibena_barva) - -# Na tomhle místě programu už se k řetězci 'modrá' nedostaneš... -``` - -Trošku zajímavější (nebo složitější?) je situace, kdy hodnotu proměnné -přepíšeš výrazem, který používá tu stejnou proměnnou. -Zkus si to: - - -```python -oblibene_cislo = 7 -print(oblibene_cislo) - -oblibene_cislo = oblibene_cislo * 6 -print(oblibene_cislo) -``` - -Co se tady děje? -Python vyhodnotí výraz za `=` se *starou* hodnotou proměnné, a teprve když -zná výsledek, přiřadí ho (a na starou hodnotu zapomene). -V našem příkladu postupuje takhle: - -```python -oblibene_cislo = oblibene_cislo * 6 -# ╰──────────┬─╯ -oblibene_cislo = 7 * 6 -# ╰─┬───╯ -oblibene_cislo = 42 -# ▲ | -# ╰──────────────────╯ -``` - - - -## Přepisování v cyklu - -Ještě „zajímavější“ je použít podobné přepisování v cyklu. - -Zopakuj si, že `for` cyklus jako: - -```python -print("Tady je pár čísel:") - -for cislo in 8, 45, 9, 21: - print(cislo) -``` - -opakuje *přiřazení do proměnné* a *tělo cyklu*; můžeš si ho rozepsat jako: - -```python -print("Tady je pár čísel:") - -cislo = 8 -print(cislo) - -cislo = 45 -print(cislo) - -cislo = 9 -print(cislo) - -cislo = 21 -print(cislo) -``` - -Zkus podobně rozepsat cyklus v následujícím programu -a popsat, co se děje: - -```python -celkem = 0 - -for delka_trasy in 8, 45, 9, 21: - print('Jdu', delka_trasy, 'km do další vesnice.') - celkem = celkem + delka_trasy - -print('Celkem jsem ušla', celkem, 'km') -``` - - -{% filter solution %} -```python -celkem = 0 - -delka_trasy = 8 -print('Jdu', delka_trasy, 'km do další vesnice.') -celkem = celkem + delka_trasy - -delka_trasy = 45 -print('Jdu', delka_trasy, 'km do další vesnice.') -celkem = celkem + delka_trasy - -delka_trasy = 9 -print('Jdu', delka_trasy, 'km do další vesnice.') -celkem = celkem + delka_trasy - -delka_trasy = 21 -print('Jdu', delka_trasy, 'km do další vesnice.') -celkem = celkem + delka_trasy - -print('Celkem jsem ušla', celkem, 'km') -``` - -Příkaz `celkem = celkem + delka_trasy` vypočítá hodnotu -`celkem + delka_trasy`, tedy přičte aktuální číslo k součtu. -Výsledek uloží opět do proměnné `celkem`. -Nová hodnota `celkem` se pak použije v dalším průchodu cyklem. - -Na začátku je `celkem` 0 a na konci se celkový součet všech čísel vypíše. -{% endfilter %} diff --git a/lessons/beginners/reassignment/info.yml b/lessons/beginners/reassignment/info.yml deleted file mode 100644 index cd9c47c7..00000000 --- a/lessons/beginners/reassignment/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Přepisování proměnných -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. -license: cc-by-sa-40 diff --git a/lessons/beginners/recursion/index.md b/lessons/beginners/recursion/index.md deleted file mode 100644 index ff673037..00000000 --- a/lessons/beginners/recursion/index.md +++ /dev/null @@ -1,148 +0,0 @@ -# Rekurze - -*Rekurze* (angl. *recursion*) je programátorská technika, -kdy funkce volá sebe sama. - -Taková rekurze skončí nekonečným voláním. -Když zadáš tento program: - -```python -def rekurzivni_funkce(): - vysledek = ... - rekurzivni_funkce() - return vysledek - -rekurzivni_funkce() -``` - -Jak to funguje? - -* Python si nadefinuje funkci `rekurzivni_funkce` -* Zavolá funkci `rekurzivni_funkce`: - * Vypočítá výsledek - * Zavolá funkci `rekurzivni_funkce`: - * Vypočítá výsledek - * Zavolá funkci `rekurzivni_funkce`: - * Vypočítá výsledek - * Zavolá funkci `rekurzivni_funkce`: - * Vypočítá výsledek - * Zavolá funkci `rekurzivni_funkce`: - * ... - * ... - * po stovkách opakování si Python všimne, že tohle asi - nikam nevede, a skončí s chybou. - -Tomu odpovídá chybová hláška: - -``` -Traceback (most recent call last): - File "/tmp/ukazka.py", line 4, in - rekurzivni_funkce() - File "/tmp/ukazka.py", line 2, in rekurzivni_funkce - return rekurzivni_funkce() - File "/tmp/ukazka.py", line 2, in rekurzivni_funkce - return rekurzivni_funkce() - File "/tmp/ukazka.py", line 2, in rekurzivni_funkce - return rekurzivni_funkce() - [Previous line repeated 996 more times] -RecursionError: maximum recursion depth exceeded -``` - -Hláška je zkrácená – dva řádky by se správně měly opakovat 999×, ale novější -verze Pythonu je vypíšou jen třikrát. - - -# Kontrolované zanoření - -Jak rekurzi využít v praxi? -Jeden způsob je si počítat, kolikrát se ještě „zanořit“. - -Představ si potápěče, který prozkoumává mořské hlubiny následujícím způsobem: - -* Jak *„prozkoumat moře“* v určité hloubce: - * Porozhlédnu se kolem - * Jsem-li už teď moc hluboko, kašlu na to; nebudu prozkoumávat dál. - * Jinak: - * Zanořím se o 10 m níž - * *Prozkoumám moře* v nové hloubce - * Zase se o 10 m vynořím - -Neboli v Pythonu: - -```python -def pruzkum(hloubka): - print(f'Rozhlížím se v hloubce {hloubka} m') - - if hloubka >= 30: - print('Už toho bylo dost!') - else: - print(f'Zanořuju se (z {hloubka} m)') - - pruzkum(hloubka + 10) - - print(f'Vynořuju se (na {hloubka} m)') - -pruzkum(0) -``` - -* Python si nadefinuje funkci `pruzkum` -* Zavolá funkci `pruzkum` s hloubkou 0: - * Vypíše `Rozhlížím se v hloubce 0 m` - * Zkontroluje, že `0 ≥ 30` (což neplatí) - * Vypíše `Zanořuju se (z 0 m)` - * Zavolá funkci `pruzkum` s hloubkou 10 m: - * Vypíše `Rozhlížím se v hloubce 10 m` - * Zkontroluje, že `10 ≥ 30` (což neplatí) - * Vypíše `Zanořuju se (z 10 m)` - * Zavolá funkci `pruzkum` s hloubkou 20 m: - * Zkontroluje, že `20 ≥ 30` (což neplatí) - * Vypíše `Zanořuju se (z 20 m)` - * Zavolá funkci `pruzkum` s hloubkou 30 m: - * Zkontroluje, že `30 ≥ 30` (což platí! konečně!) - * Vypíše `Už toho bylo dost!` - * a skončí - * Vypíše `Vynořuju se (na 20 m)` - * Vypíše `Vynořuju se (na 10 m)` - * Vypíše `Vynořuju se (na 0 m)` - - -# Lokální proměnné - -Na předchozím příkladu je vidět zajímavé chování lokálních proměnných. -Když je potápěč „na dně“, jakou hodnotu má proměnná *hloubka*? - -Tahle otázka je chyták. *Která* proměnná `hloubka`? -Když je program „na dně“, existují čtyři *různé* lokální proměnné `hloubka`: -každé zanoření, každé zavolání funkce `pruzkum`, má vlastní proměnnou. - -Podobně jako když máš globální a lokální proměnnou stejného jména, -každá funkce „vidí“ jen tu svoji proměnnou. -Ale když se potápěč vynoří a volání funkce se ukončí, tato proměnná -přestane existovat. -A „volající“ funkce vidí svoji proměnnou, ve které je stále původní hodnota. - - -# Pro matematiky - -> [note] -> Nemáš-li rád{{a}} matematiku, tuhle sekci přeskoč! - -Rekurzivní algoritmy mají původ v matematice. Faktoriál x, neboli -součin všech čísel od 1 do x, zapsaný jako x!, -matematici definují takto: - -* 0! = 1 -* Pro kladná x je x! = x · (x - 1)! - -Neboli v Pythonu: - -```python -def factorial(x): - if x == 0: - return 1 - elif x > 0: - return x * factorial(x - 1) - -print(factorial(5)) -print(1 * 2 * 3 * 4 * 5) -``` diff --git a/lessons/beginners/recursion/info.yml b/lessons/beginners/recursion/info.yml deleted file mode 100644 index 78bb5951..00000000 --- a/lessons/beginners/recursion/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Rekurze -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2019. -license: cc-by-sa-40 diff --git a/lessons/beginners/str-index-slice/index.md b/lessons/beginners/str-index-slice/index.md deleted file mode 100644 index e58e2876..00000000 --- a/lessons/beginners/str-index-slice/index.md +++ /dev/null @@ -1,208 +0,0 @@ -# Výběr z řetězců - - -Už umíš spojovat dohromady kratší řetězce: - -```python -spojeny_retezec = 'a' + 'b' -dlouhy_retezec = 'ó' * 100 -``` -Teď se podíváme na opačný proces: jak z dlouhého -řetězce dostat kratší součásti. -Začneme jednotlivými znaky. - - -## Výběr znaku - -Konkrétní znak na dané pozici se z řetězce dá vybrat operací *vybrání prvku* -(angl. *subscripting*), -která se píše podobně jako volání funkce, jen s hranatými závorkami. -Třeba takhle se dá vybrat znak na páté pozici: - -```python -pate_pismeno = 'čokoláda'[5] - -print(pate_pismeno) -``` - -Funguje to? Dostal{{a}} jsi opravdu páté písmeno? - -{% filter solution %} -Nedostal{{a}} – dostal{{a}} jsi *šesté* písmeno. -{% endfilter %} - -Jak sis možná už všiml{{a}}, programátoři počítají od nuly. -„První“ prvek má vždy číslo nula, druhý číslo jedna a tak dál. - -Stejně je to i se znaky v řetězcích. První písmeno má číslo nula, -druhé jedna, ... a osmé písmeno má číslo sedm. - -Proč je tomu tak? -K úplnému pochopení důvodů by ses potřeboval{{a}} -naučit něco o ukazatelích a polích, -což nebude hned, takže pro teď nám bude -stačit vědět, -že programátoři jsou prostě divní. - -Nebo aspoň že mají rádi divná čísla – jako nulu. - -```plain - [0] [1] [2] [3] [4] [5] [6] [7] - - ╭───┬───┬───┬───┬───┬───┬───┬───╮ - │ Č │ o │ k │ o │ l │ á │ d │ a │ - ╰───┴───┴───┴───┴───┴───┴───┴───╯ -``` - - -A když už jsme u divných čísel, -co se asi stane, když budu vybírat písmena pomocí záporných čísel? - -{% filter solution %} -```python -print('Čokoláda'[-1]) # → a -print('Čokoláda'[-2]) # → d -print('Čokoláda'[-3]) # → á -print('Čokoláda'[-4]) # → l -``` - -Záporná čísla vybírají písmenka od konce. - -```plain - [0] [1] [2] [3] [4] [5] [6] [7] - [-8][-7][-6][-5][-4][-3][-2][-1] - ╭───┬───┬───┬───┬───┬───┬───┬───╮ - │ Č │ o │ k │ o │ l │ á │ d │ a │ - ╰───┴───┴───┴───┴───┴───┴───┴───╯ -``` -{% endfilter %} - - - -## Sekání řetězců - -Kromě jednotlivých znaků můžeme vybírat i delší části – odborně -*podřetězce* (angl. *substrings*). - -Zkus, co dělá tenhle program: - -```python -retezec = 'čokoláda' -kousek = retezec[5:] -print(kousek) -``` - -{% filter solution %} -Zápis `retezec[5:]` vybere *podřetězec* od znaku číslo 5 dál. -{% endfilter %} - -Dá se použít i `retezec[:5]`, -který vybere všechno *až po* znak číslo 5. -Ne však znak 5 samotný, což je možná trochu zarážející, -ale je potřeba s tím počítat. -Poslední prvek není ve výběru obsažen, podobně jako `range(5)` neobsahuje -číslo 5. - -Ačkoli je tohle chování divné, má hezké důsledky. -Všimni si třeba, že `retezec[:5] + retezec[5:]` ti dá zpět původní `retezec`. - -Podobnému vybírání podřetězců se říká „sekání“ řetězců -(angl. *string slicing*). - -Sekání „od“ a „do“ se dá kombinovat. -Zkus si to: co asi udělají následující příkazy? - -```python -retezec = 'čokoláda' -print(retezec[:4]) -print(retezec[2:6]) -print(retezec[-3:]) -print(retezec[:]) -``` - -{% filter solution %} -Zápis `retezec[od:do]` vybere *podřetězec* od pozice `od` do pozice `do`. -Když jednu z hodnot vynecháš, vybírá se od začátku, resp. do konce. - -```python -retezec = 'čokoláda' -print(retezec[:4]) # → čoko -print(retezec[2:6]) # → kolá -print(retezec[-3:]) # → áda -print(retezec[:]) # → čokoláda -``` -{% endfilter %} - -Určování vhodných čísel, *indexů*, občas vyžaduje trochu zamyšlení. - -U sekání (s `:`) pomáhá očíslovat si „hranice“ mezi znaky, -abys v tom měl{{a}} lepší přehled: - -{{ anchor('slicing-diagram') }} -```plain - ╭───┬───┬───┬───┬───┬───┬───┬───╮ - │ Č │ o │ k │ o │ l │ á │ d │ a │ - ├───┼───┼───┼───┼───┼───┼───┼───┤ - │ │ │ │ │ │ │ │ │ - 0 1 2 3 4 5 6 7 8 - -8 -7 -6 -5 -4 -3 -2 -1 - - ╰───────────────╯ - 'čokoláda'[:4] == 'čoko' - - ╰───────────────╯ - 'čokoláda'[2:6] == 'kolá' - - ╰───────────╯ - 'čokoláda'[-3:] == 'áda' -``` - - -## Cvičení - -Zkus napsat program `zamen.py`, který umí zaměnit jedno písmeno ve slově za -jiné. Například: - -```python -slovo = input('Slovo: ') -pozice = int(input('Které písmeno zaměnit (od nuly)? ')) -novy_znak = input('Nové písmeno: ') - -... # sem doplň kód - -print(nove_slovo) -``` - -Příklad použití: - -
-Slovo: čokoláda
-Které písmeno zaměnit (od nuly)? 3
-Nové písmeno: u
-čokuláda
-
- -
-Slovo: kočka
-Které písmeno zaměnit (od nuly)? 1
-Nové písmeno: a
-kačka
-
- -Pozor na to, že řetězce v Pythonu nelze měnit. -Nemůžeš v existujícím řetězci zaměnit jeden znak za jiný; -musíš vytvořit nový řetězec poskládaný z částí toho starého. - -{% filter solution %} -```python -slovo = input('Slovo: ') -pozice = int(input('Které písmeno zaměnit (od nuly)? ')) -novy_znak = input('Nové písmeno: ') - -zacatek_slova = slovo[:pozice] -konec_slova = slovo[pozice + 1:] -nove_slovo = zacatek_slova + novy_znak + konec_slova - -print(nove_slovo) -``` -{% endfilter %} diff --git a/lessons/beginners/str-index-slice/info.yml b/lessons/beginners/str-index-slice/info.yml deleted file mode 100644 index 47fe7975..00000000 --- a/lessons/beginners/str-index-slice/info.yml +++ /dev/null @@ -1,5 +0,0 @@ -title: Výběr z řetězců -style: md -attribution: -- Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. -license: cc-by-sa-40 diff --git a/lessons/beginners/str-methods/index.md b/lessons/beginners/str-methods/index.md deleted file mode 100644 index b96ad17d..00000000 --- a/lessons/beginners/str-methods/index.md +++ /dev/null @@ -1,110 +0,0 @@ -# Řetězcové funkce a metody - -Řetězce umí všelijaké triky. -Funkcí `len()` můžeš zjistit, jak je řetězec dlouhý; -operátorem `in` pak jestli v sobě obsahuje daný podřetězec. - - - - - - - - - - - - - - - - - - - - - - -
ZápisPopisPříklad
len(r)Délka řetězcelen('čokoláda')
x in rTrue pokud je řetězec x obsažen v r'oko' in 'čokoláda'
x not in rOpak x in r'dub' not in 'čokoláda
- -Řetězce vždy berou v potaz velikost písmen, -takže např. `'ČOKO' in 'čokoláda'` je `False`. -Kdybys chtěl{{a}} porovnávat bez ohledu na velikost písmen, -musel{{a}} bys oba řetězce převést třeba na malá písmena -a pak je porovnat. - -A jak se převádí na malá písmena? -K tomu budeme potřebovat další novou vlastnost Pythonu: metody. - -## Metody - -*Metoda* (angl. *method*) je jako funkce – něco, co se dá zavolat. -Na rozdíl od funkce je svázaná s nějakým *objektem* (hodnotou). -Volá se tak, že se za objekt napíše tečka, -za ní jméno metody a za to celé se, jako u funkcí, připojí závorky -s případnými argumenty. - -Řetězcové metody `upper()` a `lower()` -převádí text na velká, respektive malá písmena. -Zkus si to! - -```python -retezec = 'Ahoj' -print(retezec.upper()) -print(retezec.lower()) -print(retezec) -``` - -> [note] -> Všimni si, že původní řetězec se nemění; metoda vrátí nový řetězec, ten -> starý zůstává. -> -> To je obecná vlastnost řetězců v Pythonu: jednou existující řetězec se už -> nedá změnit, dá se jen vytvořit nějaký odvozený. -> S touto vlastností už ses mohl{{a}} setkat při psaní funkce `zamen`. - - -### Iniciály - -Pro procvičení metod a vybírání znaků si zkus napsat program, -který se zeptá na jméno, pak na příjmení -a pak vypíše iniciály – první písmena zadaných jmen. - -Iniciály jsou vždycky velkými písmeny -(i kdyby byl uživatel líný mačkat Shift). - -{% filter solution %} -```python -jmeno = input('Zadej jméno: ') -prijmeni = input('Zadej příjmení ') -inicialy = jmeno[0] + prijmeni[0] -print('Iniciály:', inicialy.upper()) -``` - -Způsobů, jak takový program napsat, je více. -Lze například zavolat `upper()` dvakrát – zvlášť na jméno a zvlášť na příjmení. - -Nebo to jde zapsat i takto – -metoda se dá volat na výsledku jakéhokoli výrazu: - -```python -jmeno = input('Zadej jméno: ') -prijmeni = input('Zadej příjmení ') -print('Iniciály:', (jmeno[0] + prijmeni[0]).upper()) -``` - -Doporučuji spíš první způsob, ten se smysluplnými názvy proměnných. -Je sice delší, ale mnohem přehlednější. -{% endfilter %} - - -### A další - -Řetězcových metod je celá řada. -Nejužitečnější z nich najdeš v [taháku](https://pyvec.github.io/cheatsheets/strings/strings-cs.pdf), který si můžeš stáhnout či vytisknout. -Podívej se na ně a zjisti, co dělají. - -A úplně všechny řetězcové metody jsou popsány v [dokumentaci Pythonu](https://docs.python.org/3/library/stdtypes.html#string-methods) (anglicky; plné věcí, které ještě neznáš). - -Všimni si, že `len` není metoda, ale funkce; píše se `len(r)`, ne `r.len()`. -Proč tomu tak je, to za nějakou dobu poznáš. diff --git a/lessons/beginners/str-methods/info.yml b/lessons/beginners/str-methods/info.yml deleted file mode 100644 index f16a4419..00000000 --- a/lessons/beginners/str-methods/info.yml +++ /dev/null @@ -1,5 +0,0 @@ -title: Řetězcové funkce a metody -style: md -attribution: -- Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. -license: cc-by-sa-40 diff --git a/lessons/beginners/str/index.md b/lessons/beginners/str/index.md deleted file mode 100644 index 7b6b807b..00000000 --- a/lessons/beginners/str/index.md +++ /dev/null @@ -1,316 +0,0 @@ -# Zápis řetězců - -Teď se podíváme na zoubek řetězcům. -Už s nimi trochu umíš, tak začneme rekapitulací. - -Textový *řetězec* (angl. *string*) je datový typ (druh *hodnot*), -který obsahuje text – třeba slovo nebo větu. - -Když řetězec zadáváš do programu, musíš ho označit – uzavřít do -*uvozovek*, buď jednoduchých nebo dvojitých: - -```python -'tohle je řetězec' -"tohle taky" -``` - -Je velký rozdíl mezi `print('cislo')` – vypiš slovo „cislo“ – -a `print(cislo)` – vypiš hodnotu výrazu `cislo`. -Jednou je `cislo` pět konkrétních písmen; podruhé *instrukce* k použití -proměnné. -Počítač, na rozdíl od lidí, rozdíl mezi textem a instrukcí nepozná z kontextu, -a tak je uvozovky potřeba používat důsledně. - -{{ figure( - img=static('quote-comic.svg'), - alt='(Ilustrační komiks. Člověk říká robotovi: "Řekni Pavlovi, ať mi zavolá!". Robot odpoví: "PAVLOVI AŤ MI ZAVOLÁ!")', -) }} - - -## Znaky - -Texty sestávají z jednotlivých písmenek. -Řetězce víceméně taky, ale aby bylo jasné, co přesně tím *písmenkem* -myslíme, říkáme, že řetězce sestávají ze *znaků* (angl. *characters*). - -Takový znak může být písmenko (např. `A`) nebo číslice (`3`), -ale i jiný symbol (`!`). - -Každý řetězec má určitý počet znaků. -Kolik, to zjistíš pomocí funkce `len()`. -Třeba řetězec `Ahoj!` má znaků pět: - -```pycon ->>> len('Ahoj!') -5 -``` - -Jeden ze zajímavějších znaků je *mezera*. -Je to taky znak. V řetězci se tedy chová stejně jako písmenko: - -```pycon ->>> len(' ') -1 ->>> len('K ní') -4 ->>> len('3 + 2') -5 -``` - -Mimochodem, řetězec může být i prázdný – pak má nula znaků: - -```pycon ->>> len('') -0 ->>> len("") -0 -``` - - -## Uvozovky - -K uvození řetězce můžeš použít jednoduché nebo dvojité rovné uvozovky. -Není mezi nimi rozdíl. -Podobně `4.0` a `4.000` jsou dva zápisy téhož čísla, -tak `'slovo'` a `"slovo"` pro Python označuje stejnou -hodnotu, skládající se ze stejných pěti písmen. - -Použité uvozovky nejsou součástí hodnoty – python si „nepamatuje“, jakým -způsobem byl řetězec uvozen. -Když má nějaký řetězec vypsat s uvozovkami, jedny si k tomu vybere – většinou -ty jednoduché: - -```pycon ->>> "python" -'python' ->>> 'slovo' -'slovo' -``` - -> [note] -> Předchozí příklad je z interaktivního režimu Pythonu, který ukazuje hodnoty -> výrazů „programátorsky“ – pokud možno tak, jak se zapisují v Pythonu. -> Funkce `print()` vypisuje hodnoty „hezky“, „pro uživatele“ – v případě -> řetězců tedy bez uvozovek. - - -### Uvozovky v uvozovkách - -Proč si při zadávání textu můžeš vybrat mezi dvěma druhy uvozovek? - -Občas se stane, že v rámci textu potřebuješ použít samotnou uvozovku (nebo -apostrof). -Pak musíš „kolem“ řetězce použít tu druhou: - -```python -print('Zpívala si: "Tralala!"') -print("Byl to Goa'uld, parazit z planety P3X-888") -``` - -Když v rámci textu použiješ stejnou uvozovku jako „kolem něj“, tak bude Python -naprosto zmatený. - -```pycon ->>> len("Zpívala si: "Tralala"") -Traceback (most recent call last) - File "<>", line 1 - len("Zpívala si: "Tralala"") - ^ -SyntaxError: invalid syntax -``` - -Pokud používáš chytrý editor, doporučuju si zvyknout na to, jakou barvou -máš řetězce zvýrazněné. -Často to pomáhá odhalit chybky. - - -## Sekvence se zpětným lomítkem - -Co dělat, když v řetězci potřebuješ *oba* druhy uvozovek, -jako ve větě `Vtom vnuk křik': "Hleď!"`? - -Můžeš si pomoci tím, že spojíš dva řetězce: - -```pycon ->>> print("Vtom vnuk křik': " + '"Hleď!"') -Vtom vnuk křik': "Hleď!" -``` - -Ale lepší způsob je použít speciální zápis se *zpětným lomítkem*. -Kdykoli se v řetězci objeví sekvence `\'` nebo `\"`, Python dá do řetězce danou -uvozovku. - -```pycon ->>> print("Vtom vnuk křik': \"Hleď!\"") -Vtom vnuk křik': "Hleď!" ->>> print('"Jen ho nech," řek\' děd. "Kdo zná líp kraj?"') -"Jen ho nech," řek' děd. "Kdo zná líp kraj?" -``` - -Ve výsledném řetězci pak ovšem žádné zpětné lomítko *není*. -Sekvence `\'` je jen způsob, jak v Pythonu zadat `'` – jediný znak. -Tomu je celkem důležité porozumět. -Zkus si, jestli zvládneš předpovědět výsledek těchto výrazů: - -```pycon ->>> print(".\".") ->>> len(".\".") ->>> ".\"." -``` - -{% filter solution %} -```pycon ->>> print(".\".") -.". ->>> len(".\".") -3 ->>> ".\"." -'.".' -``` -{% endfilter %} - - -Znaků, které se zadávají sekvencí se zpětným lomítkem, je více. -Jedna ze zajímavějších je `\t`, představující tabulátor – jediný znak, který -se, když ho vypíšeš, „roztáhne“ na víc mezer. - -```pycon ->>> print("a\tb") # Výpis "pro lidi" -a b ->>> "a\tb" # Výpis "pro programátory" -'a\tb' ->>> len("a\tb") # Počet znaků v řetězci -3 -``` - -Se zpětným lomítkem se dá zadat jakýkoli znak – včetně *emoji* – podle jména -(`\N{…}`) nebo identifikačního čísla (`\x..`, `\u....`, `\U........`) -standardu Unicode. -Stačí přesné jméno nebo číslo znát (nebo třeba dohledat na internetu). -V následujících řetězcích jsou takové znaky pro přehlednost mezi dvěma -pomlčkami `-`. Délka každého řetězce je tedy celkem 3: - -```pycon ->>> print('-\N{GREEK CAPITAL LETTER DELTA}-') --Δ- ->>> print('-\N{SECTION SIGN}-') --§- ->>> print('-\N{GRINNING CAT FACE WITH SMILING EYES}-') --😸- ->>> print('-\x60-') --`- ->>> print('-\u30C4-') --ツ- ->>> print('-\U0001F0BD-') --🂽- -``` - - -### Zpětné lomítko - -Zpětné lomítko tedy začíná speciální sekvenci (známou pod anglickým -termínem *escape sequence*), kterou zadáš *jediný znak*. - -Tahle vychytávka má jeden, někdy nepříjemný, důsledek: pokud chceš mít jako -součást řetězce zpětné lomítko (třeba ve jménech souborů na Windows), -nemůžeš použít přímo `\`. -Musíš použít speciální sekvenci `\\` – tedy lomítko zdvojit: - -```python -print('C:\\PyLadies\\Nový adresář') -``` - -Podobně jako `\"` je zápis pro uvozovku a `\'` pro apostrof, sekvence `\\` -je zápis pro jedno zpětné lomítko. - - -### Nový řádek - -Někdy potřebuješ řetězce, které obsahují více řádků. -Pythonní řetězce ale můžeš normálně napsat jen na *jeden* řádek. -(Python se tak snaží ulehčit hledání chyby, kdybys koncovou uvozovku -zapomněl{{a}}.) - -Můžeš ale do řetězce znak pro nový řádek vložit pomocí sekvence `\n`: - -```pycon ->>> print('Haló haló!\nCo se stalo?') -Haló haló! -Co se stalo? -``` - -Ono `\n` do řetězce vloží znak nového řádku. -Ten při výpisu ukončí stávající řádek a přejde na nový – ale jinak se chová -jako jakýkoli jiný znak: - -```pycon ->>> print('-\n-') -- -- ->>> len('-\n-') -3 -``` - - -## Trojité uvozovky - -Kromě `\n` je i druhý způsob, jak zadat řetězec se znakem nového řádku: -ohraničit ho *třemi* uvozovkami (jednoduchými nebo dvojitými) -na každé straně. -Dají se tak zadávat delší víceřádkové řetězce: - -```python -basen = '''Haló haló! -Co se stalo? -Prase kozu potrkalo!''' -``` - -Pozor na to, že pokud je tenhle řetězec -v odsazeném kódu, každý jeho řádek bude začínat -několika mezerami. -(V dokumentačních řetězcích tohle nevadí, tam se s odsazením počítá.) - -```python -cislo = 4 - -if cislo > 0: - print(""" - Výsledek porovnání: - - Číslo je kladné. - """) -``` - - -## Cvičení - -Jaká je délka těchto řetězců? - -Výsledek zjistíš snadno, zkus se ale zamyslet a Python použít jen pro ověření. - -{# Highlighted as plain text to avoid spoilers #} -```plain -{# 2, 3, 4, 5 -#} -print(len('ahoj')) -print(len("""Ahoj!""")) -print(len('a b')) -print(len( ' a b ' )) -print(len('\N{SNOWMAN}ové')) -print(len('a\nb')) -print(len('a\tb')) -print(len('"\'"')) - -{# 3, 4, 5, 6 #} -print(len(""" -abc""")) - -{# 3, 5, 7, 9 #} -if True: - print(len("""a - b""")) - -{# 7, 8, 9, more #} -print(len('C:\new_dir')) - -print(len(f'{print}')) -``` diff --git a/lessons/beginners/str/info.yml b/lessons/beginners/str/info.yml deleted file mode 100644 index 9254b1d1..00000000 --- a/lessons/beginners/str/info.yml +++ /dev/null @@ -1,6 +0,0 @@ -title: Zápis řetězců -style: md -attribution: -- Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. -- Jako jeden z příkladů byl použit úryvek z povídky „Chlap, děd, vnuk, pes a hrob“ Jana Wericha. -license: cc-by-sa-40 diff --git a/lessons/beginners/testing/index.md b/lessons/beginners/testing/index.md deleted file mode 100644 index 8d19caa6..00000000 --- a/lessons/beginners/testing/index.md +++ /dev/null @@ -1,178 +0,0 @@ -# Testování - -Programátorská práce nespočívá jen v tom program napsat. -Důležité je si i ověřit, že opravdu funguje, a případně ho pak opravit. -Tomu ověřování, že program funguje, se říká *testování* (angl. *testing*). - -Zatím jsi asi svoje programy testoval{{a}} tak, že jsi -je zkusil{{a}} spustit, něco zadal{{a}} a podíval{{a}} se, -jestli jsou výsledky v pořádku. -U větších programů, které budou mít více a více -možností, ale bude těžší a těžší takhle zkontrolovat, -jestli všechny ty možnosti fungují, jak mají. - -Proto programátoři často nezkouší programy „ručně“. -Píšou jiné programy, které jejich výtvory testují za ně. - -*Automatické testy* jsou funkce, které -zkontrolují, že program funguje správně. -Spuštěním testů můžeš kdykoli ověřit, že kód funguje. -Když v otestovaném kódu v budoucnu uděláš nějakou změnu, -testy ověří, že jsi nerozbil{{a}} nic, co dříve fungovalo. - - -## Instalace knihovny pytest - -Zatím jsme v kurzu pracovali s tím, co se instaluje -se samotným Pythonem – s moduly jako `math` a `turtle`. -Kromě takových modulů ale existuje velká spousta -dalších *knihoven*, které nejsou přímo v Pythonu, ale dají se doinstalovat -a používat. - -Na testy je v samotném Pythonu zabudovaná knihovna `unittest`. -Ta je ale celkem složitá na použití, proto ji my používat nebudeme. -Nainstalujeme si knihovnu pytest, která se používá -mnohem jednodušeji a je velice populární. - -Knihovny se instalují do aktivního virtuálního prostředí. -Jak se dělá a spouští virtuální prostředí -ses naučil{{a}} při [instalaci Pythonu]({{ lesson_url('beginners/install') }}), -ale teprve teď to začíná být opravdu důležité. -Ujisti se, že máš virtuální prostředí aktivované – na začátku příkazové řádky -máš `(venv)`. - -Potom zadej následující příkaz. -(Je to příkaz příkazové řádky, podobně jako -`cd` nebo `mkdir`; nezadávej ho do Pythonu.) - -> [warning] Opisuj opatrně! -> Příkaz níže instaluje software z internetu. -> Nahrát takovou knihovnu na internet může kdokoli – hodný nebo zlý, -> chytrý nebo hloupý. -> Za knihovnu `pytest` autoři tohoto kurzu ručí. -> Jiné knihovny ale můžou dělat neplechu nebo být dokonce „zavirované“; -> už při instalaci můžou něco pokazit. -> Dej si proto pozor a ve jménu `pytest` neudělej překlep! -> -> Nainstaluješ-li přesto omylem něco, cos nechtěl{{a}}, dej co nejdřív vědět -> zkušenějšímu programátorovi, aby zkontroloval, jaký to mohlo mít efekt. - -```console -(venv)$ python -m pip install pytest -``` - -> [note] Co ten příkaz znamená? -> `python -m pip` zavolá Python s tím, že má pustit modul -> `pip`. Tento modul umí instalovat nebo -> odinstalovávat knihovny. -> (Jestli si pamatuješ vytváření virtuálního prostředí, použil{{a}} jsi tam -> příkaz `python -m venv` – modul `venv` umí vytvářet virtuální prostředí.) -> No a slova `install pytest` říkají Pipu, že má nainstalovat `pytest`. -> -> Nápověda k použití Pipu se dá vypsat pomocí příkazu -> `python -m pip --help`. - -> [warning] Pro Windows -> Jsi-li na Windows, od této lekce začne být důležité -> spouštět pythonní programy pomocí `python program.py`, ne jen -> `program.py`. -> Ačkoli se v těchto materiálech všude používá `python` na začátku, zatím -> mohlo všechno fungovat i bez toho. -> Program se ale bez příkazu `python` může spustit v jiném Pythonu, -> než v tom z virtuálního prostředí – a tam `pytest` nebude k dispozici. - - -## Psaní testů - -Nejdříve si testování ukážeme na jednoduchém příkladu. -Tady je funkce `secti`, která umí sečíst -dvě čísla, a další funkce, která testuje, jestli se -`secti` pro určité hodnoty chová správně. - -Kód si opiš do souboru `test_secteni.py` v novém prázdném adresáři. -Jméno je důležité: `pytest` ve výchozím nastavení předpokládá, -že jména jak souborů s testy tak samotných testovacích funkcí začínají na -`test_`. - -```python -def secti(a, b): - """Vrátí součet dvou čísel""" - return a + b - -def test_secti(): - """Otestuje funkci secti""" - assert secti(1, 2) == 3 -``` - -Co se v té testovací funkci děje? - -Příkaz `assert` vyhodnotí výraz za ním a pokud výsledek není pravdivý, -vyvolá výjimku, která způsobí, že test selže. -Můžeš si představit, že `assert a == b` dělá následující: - -```python -if not (a == b): - raise AssertionError('Test selhal!') -``` - -> [note] -> Zatím `assert` nepoužívej jinde než v testovacích funkcích. -> V „normálním” kódu se `assert` může chovat trochu jinak než výše, -> ale do toho teď nebudeme zabředávat. - - -## Spouštění testů - -Testy se spouští zadáním příkazu -`python -m pytest -v` následovaným názvem souboru s testy. -Tedy v překladu: Pythone, pusť -modul pytest, -v „ukecaném” režimu (angl. verbose) a se zadaným souborem. - -```console -$ python -m pytest -v test_secteni.py -``` - -```pytest -============================= test session starts ============================== -platform linux -- Python 3.7.1, pytest-3.6.4, py-1.5.4, pluggy-0.6.0 -- venv/bin/python -cachedir: .pytest_cache -rootdir: naucse, inifile: -collecting ... collected 1 item - -test_secteni.py::test_secti PASSED [100%] - -=========================== 1 passed in 0.00 seconds =========================== -``` - -Tento příkaz projde zadaný soubor, zavolá v něm všechny funkce, -jejichž jméno začíná na `test_`, a ověří, že nevyvolají žádnou -výjimku – typicky výjimku z příkazu `assert`. -Pokud výjimka nastane, dá to `pytest` velice červeně -najevo a přidá několik informací, které můžou -usnadnit nalezení a opravu chyby. - -> [note] -> Argument s názvem souboru můžeš vynechat: `python -m pytest -v`. -> V takovém případě `pytest` projde aktuální adresář a spustí testy -> ze všech souborů, jejichž jméno začíná na `test_`. Místo souboru -> lze též uvést adresář: `pytest` vyhledá testy v něm. - -Zkus si změnit funkci `secti` (nebo její test) a podívat se, -jak to vypadá, když test „neprojde“. - - -## Testovací moduly - -Testy se většinou nepíšou přímo ke kódu, -ale do souboru vedle. -Je to tak přehlednější a taky to pak zjednodušuje -*distribuci* – předání kódu někomu, kdo ho chce -jen spustit a testy nepotřebuje. - -Rozděl soubor s testem sečítání: funkci `secti` přesuň do modulu `secteni.py` -a v `test_secteni.py` nech jenom test. -Do `test_secteni.py` pak na začátek přidej `from secteni import secti`, -aby byla funkce testu k dispozici. - -Test by měl opět projít. diff --git a/lessons/beginners/testing/info.yml b/lessons/beginners/testing/info.yml deleted file mode 100644 index 2fe54ecb..00000000 --- a/lessons/beginners/testing/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Testování -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2020. -license: cc-by-sa-40 diff --git a/lessons/beginners/tuple/index.md b/lessons/beginners/tuple/index.md deleted file mode 100644 index 1a8812ce..00000000 --- a/lessons/beginners/tuple/index.md +++ /dev/null @@ -1,150 +0,0 @@ -## N-tice - -Když už známe seznam, podívejme se na jeho sestřičku: takzvanou -n-tici (angl. *tuple*). - -N-tice může, podobně jako seznam, obsahovat n prvků. -N-tice se dvěma prvky je *dvojice* -neboli *pár* (angl. *pair*); se třemi -prvky *trojice* (angl. *3-tuple*), -se čtyřmi *čtveřice* (angl. *4-tuple*), atd. - -> [note] -> Pro úplnost: existují i n-tice s jedním prvkem (hmm… „jednice”?) -> a s nula prvky (prázdné n-tice, angl. *empty tuple*), -> ale ty se v praxi tolik nepoužívají. - -N-tice se tvoří jako seznamy, jen kolem sebe nemají hranaté závorky. -Stačí čárky mezi prvky: - -```python -dvojice = 'Pat', 'Mat' -print(dvojice) -``` - -Chovají se skoro stejně jako seznamy, jen nejdou měnit. -Nemají tedy metody jako `append` a `pop` a nedá se jim přiřazovat do prvků -(např. `ntice[1] = 2`). -Dají se ale použít v cyklu `for` a dají se z nich číst jednotlivé prvky -(např. `print(ntice[1])`). - -```python -osoby = 'máma', 'teta', 'babička' -for osoba in osoby: - print(osoba) - -prvni = osoby[0] -print(f'První je {prvni}') -``` - -> [note] -> Vypadá to povědomě? Aby ne! -> N-tice jsme už použil{{gnd('i', 'y', both='i')}} dříve: -> `for pozdrav in 'Ahoj', 'Hello', 'Ciao':` -> ve skutečnosti používá n-tici. - -Když chceš n-tici předat do funkce, -narazíš na problém, že čárka odděluje jednotlivé -argumenty funkce. -V podobných případech musíš n-tici -uzavřít do závorek, aby bylo jasné, že jde o jednu -hodnotu (byť složenou). - -```python -print('osoby:', ('máma', 'teta', 'babička')) -``` - -```python -seznam_dvojic = [] -for i in range(10): - # `append` bere jen jeden argument; dáme mu jednu dvojici - seznam_dvojic.append((i, i**2)) -print(seznam_dvojic) -``` - -N-tice se hodí, pokud chceš z funkce vrátit -víc než jednu hodnotu. -Když u příkazu `return` použiješ několik hodnot oddělených čárkou, -vypadá to, že vracíš několik hodnot, ale -ve skutečnosti se vrací jen jedna n-tice. - -```python -def podil_a_zbytek(a, b): - return a // b, a % b -``` - -> [note] -> Funkce „podíl a zbytek“ je mimochodem k dispozici přímo v Pythonu -> pod jménem `divmod`. - -Python umí ještě jeden trik, takzvané „rozbalení“ (angl. *unpacking*). -Když chceš přiřadit do několika proměnných najednou, stačí je na levé -straně rovnítka oddělit čárkou a na pravou stranu -dát nějakou „složenou” hodnotu – třeba právě n-tici. - -```python -podil, zbytek = podil_a_zbytek(12, 5) -``` - -> [note] -> N-tice se k „rozbalování“ hodí nejvíc, protože mají -> daný počet prvků. -> Jde to ale použít se všemi hodnotami, které jdou použít ve `for`: -> -> ```python -> ix, ocko = 'xo' -> jedna, dva, tri = [1, 2, 3] -> ``` - - -## Malé n-tice - -Jak vytvořit n-tici s žádným nebo jedním prvkem? Takhle: - -```python -prazdna_ntice = () -jednoprvkova_ntice = ('a', ) -``` - -Druhý příklad jde i bez závorek – -`jednoprvkova_ntice = 'a',` – -ale to vypadá jako zapomenutá čárka. -Když budeš *opravdu* potřebovat jednoprvkovou -n-tici, radši ji pro přehlednost ozávorkuj. - - -## Kdy použít seznam a kdy n-tici? - -Seznamy se používají, když předem nevíš, -kolik v nich přesně bude hodnot, -nebo když je hodnot mnoho. -Například seznam slov ve větě, -seznam účastníků soutěže, seznam tahů ve hře -nebo seznam karet v balíčku. -Oproti tomu `for pozdrav in 'Ahoj', 'Hello', 'Hola', 'Hei', 'SYN':` -používá n-tici. - -Když se počet prvků může v průběhu programu měnit, určitě sáhni po seznamu. -Příklad budiž seznam přihlášených uživatelů, nevyřešených požadavků, -karet v ruce nebo položek v inventáři. - -N-tice se často používají na hodnoty -různých typů, kdy má každá „pozice” -v n-tici úplně jiný význam. -Například seznam můžeš použít na písmena abecedy, -ale dvojice „podíl a zbytek“ je n-tice. - -Prázdné n-tice a n-tice s jedním -prvkem se zapisují trochu divně a má to své důvody: -může-li nastat situace, kdy takovou sekvenci budeš -potřebovat, většinou je lepší sáhnout po seznamu. -Například seznam hracích karet v ruce nebo -seznam lidí aktuálně sledujících video může být občas prázdný. - -Seznamy i n-tice mají i technické limity: -n-tice nejdou měnit a až se naučíš pracovat se slovníky, -zjistíš, že seznamy tam nepůjdou použít jako klíče. -V takových případech je potřeba použít ten druhý typ sekvence. - -Často není úplně jasné, který typ použít. -V takovém případě je to pravděpodobně jedno. diff --git a/lessons/beginners/tuple/info.yml b/lessons/beginners/tuple/info.yml deleted file mode 100644 index 7edb54f9..00000000 --- a/lessons/beginners/tuple/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: N-tice -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/variables/index.md b/lessons/beginners/variables/index.md deleted file mode 100644 index dd4f2000..00000000 --- a/lessons/beginners/variables/index.md +++ /dev/null @@ -1,205 +0,0 @@ -# Čtverec - -Teď se vrátíme do základní školy a zkusíme si napsat program, -který vypočítá obsah a obvod čtverce, u kterého známe délku strany. - -Vytvoř si v editoru nový soubor a ulož ho do adresáře pro dnešní lekci -pod jménem `ctverec.py`. -Zkus do něj napsat program, který spočítá a vypíše obvod a obsah čtverce -se stranou 356 cm. - -Po spuštění by se mělo vypsat něco jako: - -``` -Obvod čtverce se stranou 356 cm je 1424 cm -Obsah čtverce se stranou 356 cm je 126736 cm2 -``` - -Pro připomenutí, obvod čtverce se stranou a -se dá vypočítat jako O = 4a -a obsah jako S = a². - -> [note] Matematika! -> Doufám, že tenhle příklad nikoho neodradí, -> ale „počítač“ je holt od slova *počítat*. -> Není třeba se děsit; -> na základy programování si vystačíme s matematickými -> znalostmi ze základní školy. - -Výsledky by měl spočítat Python; číslo 1424 nebo 126736 přímo do programu nepiš.
-Jestli si nevíš rady, podívej se na program printing.py -z [lekce o `print`]({{ lesson_url('beginners/print') }}), kde jeden řádek dělá něco podobného. - -Až budeš mít program hotový, nebo až budeš chtít vyzkoušet rozepsaný kousek, -spusť ho: -* pokud ti už na začátku příkazové řádky nesvítí `(venv)`, - aktivuj si virtuální prostředí, -* pomocí `cd` donaviguj do adresáře s programem, -* zadej `python ctverec.py`. - -Funguje? Jestli ne, oprav ho a zkus to znovu! -Když už jsi v příkazové řádce ve správném adresáři, spuštění znamená zadat -znovu příkaz `python ctverec.py`. -Abys to nemusel{{a}} celé psát, můžeš se k předchozímu příkazu vrátit -pomocí šipky nahoru, . - -{% filter solution %} - Program, který vypíše správný výsledek, může vypadat třeba takhle: - - ```python - print('Obvod čtverce se stranou 356 cm je', 4 * 356, 'cm') - print('Obsah čtverce se stranou 356 cm je', 356 * 356, 'cm2') - ``` -{% endfilter %} - - -## Menší čtverec - -Jestli všechno funguje, zkus změnit program tak, -aby počítal obsah a obvod čtverce o straně 123 cm. - -{% filter solution %} - ```python - print('Obvod čtverce se stranou 123 cm je', 4 * 123, 'cm') - print('Obsah čtverce se stranou 123 cm je', 123 * 123, 'cm2') - ``` -{% endfilter %} - - -## Proměnné - -Zvládneš to i pro stranu 3945 cm, 832 cm, 956 cm? -Baví tě přepisování čísel? -Kdyby byl program delší (několikastránkový), -jak bys zajistil{{a}}, že jedno z těch čísel nepřehlédneš? - -Existuje způsob, jak program napsat, -aniž bys musela pokaždé přepisovat všechna čísla: -stranu čtverce si „pojmenuješ“ a potom používáš jenom její jméno. -V Pythonu na pojmenovávání hodnot slouží *proměnné* (angl. *variables*). -Používají se takto: - -```python -strana = 123 -print('Obvod čtverce se stranou', strana, 'je', 4 * strana, 'cm') -print('Obsah čtverce se stranou', strana, 'je', strana * strana, 'cm2') -``` - -Neboli: napíšeš jméno, pak rovnítko a za rovnítkem výraz, -jehož hodnota se do proměnné *přiřadí*. -Když potom napíšeš jméno proměnné ve výrazu, -použije se zapamatovaná hodnota. - -> [style-note] -> Je zvykem dát před i za rovnítko po jedné mezeře. - -To nás vede k jedné ze základních programátorských -zásad: „neopakuj se“ (anglicky *Don't repeat yourself*, DRY). -Když se někde opakuje stejná hodnota, stejný výraz -nebo stejný kus kódu, -{{ gnd('dobrý programátor', 'dobrá programátorka', both='dobrý programátor') }} -ten kus programu *pojmenuje* -a dál pak používá jen jméno. -Často se totiž stává, že je program potřeba změnit – -buď je v něm chyba nebo se změní zadání. -A potom je mnohem jednodušší změnu udělat jen na jednom místě. - -Kromě toho dobrá jména usnadňují čtení programu: -u `4 * 183` není moc jasné, co ta čísla znamenají. -Výraz `4 * strana` je na tom mnohem líp. - - -> [extra-activity] -> -> ## Kruhy -> -> *Tohle je příklad navíc! Klidně ho přeskoč.* -> -> Změna zadání! -> Zkus program doplnit tak, aby kromě čtverce počítal -> i obvod a obsah kruhu se stejným poloměrem, -> jakou má čtverec stranu. -> -> Pro připomenutí, obvod kruhu s poloměrem r -> je o = 2πr, obsah S = πr² -> a π je zhruba 3,1415926. -> -> Všechna čísla, která matematici označují jen jedním -> písmenkem (klidně řeckým), vhodně pojmenuj. - - -## Komentáře - -Program si teď zpřehledníme *komentářem*. -V Pythonu komentář začíná dvojkřížkem (`#`), -za který můžeš napsat úplně cokoliv – až do konce -řádku bude Python všechno ignorovat. - -Komentáře jsou důležité: programy nečte jen počítač, ale i lidé. -Do komentářů si můžeš poznamenat, co dělá celý program, -vysvětlit, jak funguje nějaká složitější část, -nebo vyjasnit něco, co není jasné přímo z programu. - -Vždycky, když píšeš program, snaž se vžít do role někoho, -kdo potom ten program bude číst, -a všechno, co by mohlo být nejasné, upřesnit v komentářích. -(Nejčastěji to budeš číst {{ gnd('sám', 'sama') }}, třeba po několika měsících, -takže tím pomáháš {{ gnd('sám', 'sama') }} sobě!) - -```python -# Tento program počítá obvod a obsah čtverce. - -strana = 123 # v centimetrech -print('Obvod čtverce se stranou', strana, 'je', 4 * strana, 'cm') -print('Obsah čtverce se stranou', strana, 'je', strana * strana, 'cm2') -``` - -> [style-note] -> Píšeš-li komentáš na stejný řádek jako kód, je zvykem před `#` dát dvě -> mezery (nebo i víc). -> Za `#` pak patří právě jedna. - -## Načítání vstupu - -Nakonec se podíváme, jak zařídit, aby číslo nemuselo být -zapsáno v programu, ale aby ho mohl uživatel zadat sám. - -Stejně jako když ses naučil{{a}} používat `print` -i tady jen řeknu, že na to použijeme *funkce*. -Detaily si vysvětlíme později; -pro teď to budou kouzelná zaříkadla. - -> [warning] -> Pozor, záleží na typu hodnoty, který chceš získat: text nebo číslo. -> Vybírej pečlivě! - -* Chceš-li načíst **text**, použij: - - ```python - promenna = input('Zadej text: ') - ``` - -* Chceš-li načíst **celé číslo**, použij: - - ```python - promenna = int(input('Zadej číslo: ')) - ``` - -* Chceš-li načíst **desetinné číslo**, použij: - - ```python - promenna = float(input('Zadej číslo: ')) - ``` - -Místo textu `'Zadej …'` se dá napsat i jiná výzva. -A výsledek se samozřejmě dá uložit i do jiné proměnné než `promenna`. - -Hotový program může vypadat takto: - -```python -# Tento program počítá obvod a obsah čtverce. - -strana = float(input('Zadej stranu čtverce v centimetrech: ')) -print('Obvod čtverce se stranou', strana, 'je', 4 * strana, 'cm') -print('Obsah čtverce se stranou', strana, 'je', strana * strana, 'cm2') -``` diff --git a/lessons/beginners/variables/info.yml b/lessons/beginners/variables/info.yml deleted file mode 100644 index d29a5b8f..00000000 --- a/lessons/beginners/variables/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Proměnné -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/venv-setup/index.md b/lessons/beginners/venv-setup/index.md deleted file mode 100644 index 18a21ded..00000000 --- a/lessons/beginners/venv-setup/index.md +++ /dev/null @@ -1,222 +0,0 @@ -{%- macro sidebyside(titles=['Unix', 'Windows'], code=True) -%} -
- {%- for title in titles -%} -
-

{{ title }}

-{%- filter markdown() -%} -{%- if code -%}``` - {%- if title.lower().startswith('win') -%}dosvenv{%- else -%}console{%- endif -%} -{%- endif -%} -{{ caller() | extract_part(loop.index0, '---') | dedent }} -{%- if code -%}```{%- endif -%} -{%- endfilter -%} -
- {%- endfor -%} -
-{%- endmacro -%} - -# Nastavení prostředí - -V této sekci si: -* připravíš adresář, do kterého budeš ukládat soubory k začátečnickým kurzům - Pythonu a -* aktivuješ virtuální prostředí. - -## Příprava adresáře - -Programátoři vytváří spoustu souborů a víc než u mnoha jiných uživatelů -počítače jim záleží na tom, kde jsou ty soubory uložené. - -Níže uvedený postup zdaleka není jediná možnost, jak si organizovat soubory. -Když ale použiješ tenhle ozkoušený způsob, -může to hodně zjednodušit život těm, kteří ti budou pomáhat -s případnými problémy. - -{%- if var('pyladies') -%} -{% set rootname = 'pyladies' %} -{%- else -%} -{% set rootname = 'naucse-python' %} -{%- endif %} - -Nejdřív vytvoř adresář (složku), ve kterém budeš mít soubory ke kurzu Pythonu. -Může to být třeba `{{ rootname }}` ve tvém domovském adresáři. -(Můžeš ho pojmenovat i jinak, ale `{{ rootname }}` používají příklady níže.) - -Zvolený adresář po vytvoření nesmíš přesouvat jinam. -Proto ho nedoporučuji vytvářet na Ploše. - -> [note] -> Kdybys někdy adresář přece jen přesunul{{a}} jinam, -> přestane fungovat *virtuální prostředí*, které za chvíli vytvoříme. -> Musel{{a}} bys ho smazat a vytvořit nové. - -Tenhle adresář budeš potřebovat na celý zbytek kurzu i na případné -navazující kurzy. -Poznamenej si proto kde přesně je – zkopíruj si celé jeho jméno, které pak -můžeš vložit do `cd` v příkazové řádce nebo do grafického prohlížeče souborů. - - -### Adresář pro každou lekci - -Nový adresář je zatím prázdný. -To se ale brzo změní a čím víc věcí v něm bude, tím bude důležitější -mít obsah zorganizovaný. - -Pro začátek si budeme tvořit nový podadresář pro každou lekci tohoto kurzu. -Aby byly tyhle adresáře hezky seřazené, budeme je číslovat: -tahle první lekce bude mít číslo `00`, -příště si vytvoříš adresář `01` a tak dále. - -Všechny budou v tvém novém adresáři, který jsi vytvořil{{a}} před chvilkou. - -Adresář `00` si vytvoř už teď. -(Možná do něj dnes nic nedáš, ale hodí se ho mít jako ukázku pro příště.) - - -### Přepnutí - -Pak otevři příkazovou řádku a příkazem `cd` přepni do adresáře, -ve kterém jsi právě vytvořil{{a}} `00` (t.j. ne přímo do `00`). -Například: - -```console -$ cd {{ rootname }} -``` - -Pak zkontroluj, že jsi na správném místě: -* Pomocí příkazu `pwd` (na Windows `cd`) zkontroluj, - že opravdu jsi v nově vytvořeném adresáři. -* Pomocí příkazu `ls` (na Windows `dir`) zkontroluj, - že v něm je podadresář `00`. - -Například: - -{% call sidebyside(titles=['Unix (Linux, macOS)', 'Windows']) %} -$ pwd -/home/helena/{{rootname}} - -$ ls -00 ---- -> cd -C:\Users\Helena\{{rootname}} - -> dir - Directory of C:\Users\Helena\{{rootname}} -05/08/2014 07:28 PM 00 -{% endcall %} - - -## Virtuální prostředí - -Teď si vytvoříš *virtuální prostředí* pro Python. - -Virtuální prostředí je něco, co zajistí, že se všechny počítače budou -chovat zhruba stejně. -Až ho zprovozníme, nebudu už potřebovat instrukce zvlášť pro Linux, -zvlášť pro Windows a zvlášť pro macOS. - -> [note] -> V budoucnu využiješ i druhou výhodu: každé virtuální prostředí je oddělené od -> ostatních, takže když doinstaluješ nějakou knihovnu (rozšíření pro Python), -> projeví se to jen v jednom virtuálním prostředí. -> Kdyby se pak při práci na projektu něco pokazilo, neohrozí to další -> projekty ve tvém počítači. - -Jak na to? -Na každém systému jinak! - -* **Linux**: - - Podle toho, jak máš Python nainstalovaný, bude fungovat jeden z následujících - příkazů. - Bude je rychlejší vyzkoušet než popsat, kdy je který správný, - takže nejdřív zkus: - - ```console - $ python3 -m venv venv - ``` - - A jestli dostaneš chybu `No module named venv`, zkus místo toho: - - ```console - $ virtualenv -p python3 venv - ``` - -* **macOS**: - - ```console - $ python3 -m venv venv - ``` - -* **Windows**: - - Podle toho, jak máš Python nainstalovaný, bude fungovat jeden z následujících - příkazů. - Bude je rychlejší vyzkoušet než popsat, kdy je který správný, - takže nejdřív zkus: - - ```doscon - > py -3 -m venv venv - ``` - - A jestli dostaneš chybu `'py' is not recognized`, zkus místo toho: - - ```doscon - > python3 -m venv venv - ``` - -Tím se ti vytvořil adresář `venv`, který virtuální prostředí obsahuje. -Můžeš se podívat dovnitř, ale neukládej tam své soubory a nikdy tam nic neměň! - -Zkontroluj si, že `00` a `venv` jsou pěkně vedle sebe: - -{% call sidebyside(titles=['Unix', 'Windows']) %} -$ ls -00 -venv ---- -> dir - Directory of C:\Users\Helena\{{rootname}} -05/08/2014 07:28 PM 00 -05/08/2014 07:38 PM venv -{% endcall %} - -V grafickém prohlížeči souborů to vypadá např. takto: - -{{ figure( - img=static('dirs-00.png'), - alt="(adresáře '00' a 'venv' vedle sebe)", -) }} - - -### Aktivace virtuálního prostředí - -Nakonec virtuální prostředí aktivuj: - -{% call sidebyside(titles=['Unix', 'Windows'], code=False) %} -```console -$ source venv/bin/activate -``` ---- -```doscon -> venv\Scripts\activate -``` - -Jestli používáš příkazovou řádku ve Visual Studio Code, -je příkaz pro Windows složitější: -```doscon -> &powershell -ExecutionPolicy bypass -> venv/Scripts/Activate.ps1 -``` -{% endcall %} - -Po spuštění tohoto příkazu by se mělo na začátku příkazové řádky -(před `$` nebo `>`) objevit slovo `(venv)`. -Tak poznáš, že je virtuální prostředí *aktivní*. - -Aktivační příkaz si zapiš. - -Vždycky, když pustíš příkazovou řádku, ve které budeš zkoušet své programy, -budeš muset pomocí `cd` přepnout do `{{rootname}}` a zadat tento -aktivační příkaz. diff --git a/lessons/beginners/venv-setup/info.yml b/lessons/beginners/venv-setup/info.yml deleted file mode 100644 index 311a553e..00000000 --- a/lessons/beginners/venv-setup/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Nastavení prostředí -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2018. -license: cc-by-sa-40 diff --git a/lessons/beginners/while/index.md b/lessons/beginners/while/index.md deleted file mode 100644 index 3c7015a1..00000000 --- a/lessons/beginners/while/index.md +++ /dev/null @@ -1,108 +0,0 @@ -# While - -Kromě cyklu `for` máme ještě druhý typ cyklu: `while` (angl. *dokud*). -Na rozdíl od `for`, kde *předem známe počet opakování*, -se while používá, když cyklus závisí na nějaké podmínce. -Tělo cyklu se opakuje, dokud je podmínka splněna. -Zkus si naprogramovat následující postup pro zubaře: - -* Řekni, aby pacient řekl „Ááá“, a počkej na odpověď -* Dokud pacient *ne*řekl „Ááá“: - * Vynadej pacientovi - * Znovu počkej na odpověď - -```python -odpoved = input('Řekni Ááá! ') -while odpoved != 'Ááá': - print('Špatně, zkus to znovu') - odpoved = input('Řekni Ááá! ') -``` - -Ale pozor! Je velice jednoduché napsat cyklus, -jehož podmínka bude splněna vždycky. -Takový cyklus se bude opakovat donekonečna. - -* Dokud je pravda pravdivá: - * Napiš náhodné číslo - * Napiš hlášku - -```python -from random import randrange - -while True: - print('Číslo je', randrange(10000)) - print('(Počkej, než se počítač unaví...)') -``` - -Program se dá přerušit zmáčknutím -Ctrl+C. - -> [note] -> Tahle klávesová zkratka vyvolá v programu chybu -> a program se – jako po každé chybě – ukončí. - -A nakonec, existuje příkaz `break`, který z cyklu „vyskočí“: -začnou se hned vykonávat příkazy za cyklem. - -```python -while True: - odpoved = input('Řekni Ááá! ') - if odpoved == 'Ááá': - print('Bééé') - break - print('Špatně, zkus to znovu') - -print('Hotovo, ani to nebolelo.') -``` - -Příkaz `break` se dá použít jenom v cyklu (`while` nebo `for`) -a pokud máš víc cyklů zanořených v sobě, vyskočí jen z toho vnitřního. - -```python -for i in range(10): # Vnější cyklus - for j in range(10): # Vnitřní cyklus - print(j * i, end=' ') - if i <= j: - break - print() -``` - -Ale zpátky k `while`! -Dokážeš napsat tenhle program? - -## Oko bere - -* Začínáš s 0 body. -* Počítač v každém kole vypíše, kolik máš bodů, - a zeptá se tě, jestli chceš pokračovat. -* Pokud odpovíš „ne“, hra končí. -* Pokud odpovíš „ano“, počítač „otočí kartu“ - (náhodně vybere číslo od 2 do 10), vypíše její hodnotu a přičte ji k bodům. -* Pokud máš víc než 21 bodů, prohráváš. -* Cílem hry je získat co nejvíc bodů, ideálně 21. - -{% filter solution %} -```python -from random import randrange - -soucet = 0 -while soucet < 21: - print('Máš', soucet, 'bodů') - odpoved = input('Otočit kartu? ') - if odpoved == 'ano': - karta = randrange(2, 11) - print('Otočil{{a}} jsi', karta) - soucet = soucet + karta - elif odpoved == 'ne': - break - else: - print('Nerozumím! Odpovídej "ano", nebo "ne"') - -if soucet == 21: - print('Gratuluji! Vyhrál{{a}} jsi!') -elif soucet > 21: - print('Smůla!', soucet, 'bodů je moc!') -else: - print('Chybělo jen', 21 - soucet, 'bodů!') -``` -{% endfilter %} diff --git a/lessons/beginners/while/info.yml b/lessons/beginners/while/info.yml deleted file mode 100644 index 5e8b6184..00000000 --- a/lessons/beginners/while/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Cyklus While -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017. -license: cc-by-sa-40 diff --git a/lessons/beginners/with/index.md b/lessons/beginners/with/index.md deleted file mode 100644 index 18de077f..00000000 --- a/lessons/beginners/with/index.md +++ /dev/null @@ -1,62 +0,0 @@ - -# Kontext: `with` a `finally` - -> [note] -> Čteš-li tyto materiály poprvé, tuto sekci můžeš s klidným svědomím přeskočit. -> Pokročilejším ale doporučuju vsadit věci do širšího kontextu. - -Příkaz `with` pracuje s tzv. *kontextem* (tady s kontextem *otevřeného -souboru*), který má začátek a konec a při ukončení je potřeba něco udělat -(tady zavřít soubor). - -Kontext je v podstatě zkratka pro `try`/`finally`. Pamatuješ si na `finally`? - -Toto: - -```python -with open('basnicka.txt', encoding='utf-8') as soubor: - # Zpracování souboru - obsah = soubor.read() -``` - -je zkratka pro: - -```python -soubor = open('basnicka.txt', encoding='utf-8') -try: - # Zpracování souboru - obsah = soubor.read() -finally: - # Ukončení kontextu - soubor.close() -``` - -Jak `with` tak `finally` zaručí, že se soubor vždy uzavře: -když se zpracování povede, ale i když v něm nastane výjimka, -nebo když z něj vyskočíš pomocí `return` nebo `break`: - -```python -def nacti_cele_cislo(jmeno_souboru): - with open(jmeno_souboru, encoding='utf-8') as soubor: - return int(soubor.read()) - # I když "return" vyskočí z funkce (nebo int() zbůsobí ValueError), - # soubor se zavře. - - -# Pro vyzkoušení napiš do souboru 'cislo.txt' nějaké číslo. -print(nacti_cele_cislo('cislo.txt') * 11) -``` - - -## Kontrola výjimek - -Chování příkazu `with` závisí na objektu, se kterým jej použiješ. -Pro soubor – výsledek funkce `open` – se soubor na konci bloku zavře. -Podobných „samozavíracích“ objektů, které se dají použít s `with`, existuje -v různých knihovnách víc. -Typické jsou objekty, které se starají o připojení např. k jinému počítači -nebo k databázi, kdy je po práci dobré spojení ukončit a „uklidit po sobě“. - -Z lekce o testování si možná pamatuješ `with pytest.raises(...):`. -Výsledek `pytest.raises` na konci bloku `with` kontroluje, že v rámci bloku -nastala správná výjimka. diff --git a/lessons/beginners/with/info.yml b/lessons/beginners/with/info.yml deleted file mode 100644 index a13678b0..00000000 --- a/lessons/beginners/with/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: "Kontext: with a finally" -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2021. -license: cc-by-sa-40 diff --git a/lessons/beginners/zip-enumerate/index.md b/lessons/beginners/zip-enumerate/index.md deleted file mode 100644 index 0fa5fb39..00000000 --- a/lessons/beginners/zip-enumerate/index.md +++ /dev/null @@ -1,259 +0,0 @@ -# Iterátory n-tic - -Některé hodnoty v Pythonu jsou *iterovatelné* (angl. *iterable*): -obsahují sekvenci jiných hodnot a lze je „projít“ (iterovat) cyklem `for` nebo -je převést na seznam. -Už jich známe několik: - -```pycon ->>> list(range(10)) # sekvence čísel ->>> list('ahoj') # řetězec ->>> list(['Ahoj', 'Hello', 'Hei']) # seznam ->>> list((12, 'Sr', True)) # n-tice -``` - -Spousta těchto typů umí něco navíc: zjistit jestli obsahují nějaký prvek -(`4 in range(10)`), zjistit délku (`len([1, 2, 3])`), převést na velká písmena -(`'abc'.upper()`). -Nic z toho ale není potřeba, aby byl objekt iterovatelný. - -Podívejme se na dva další iterovatelné objekty: `enumerate` a `zip`. -Oba jsou sekvence vytvořené z jiných, jednodušších sekvencí. - - -## Enumerate: očíslování sekvence - -Funkce `enumerate` vezme nějakou existující sekvenci a *očísluje ji*: -ve vrácené sekvenci budou dvojice (index, původní hodnota). - -Řekněme, že máš tento seznam: - -```python -trpaslici = ['Prófa', 'Stydlín', 'Dřímal', 'Kejchal', 'Štístko', - 'Šmudla', 'Rejpal'] -``` - -Když na něj použiješ `enumerate`, dostaneš objekt `enumerate`, -který podobně jako `range()` neukáže svůj obsah rovnou, -ale můžeš se „do něj podívat“ převedením na seznam. -Uvidíš tak seznam dvojic (číslo, trpaslík): - -```pycon ->>> enumerate(trpaslici) - ->>> list(enumerate(trpaslici)) -[(0, 'Prófa'), (1, 'Stydlín'), (2, 'Dřímal'), (3, 'Kejchal'), (4, 'Štístko'), (5, 'Šmudla'), (6, 'Rejpal')] -``` - -Místo převedení na seznam můžeš přes objekt `enumerate` iterovat cyklem `for` -a pro každou dvojici něco udělat. -Třeba ji hezky vypsat: - -```python -for dvojice in enumerate(trpaslici): - # Rozbalení dvojice - index, trpaslik = dvojice - # Vypsání - print(f'Na pozici {index} je {trpaslik}!') -``` - -Objekt, který funkce `enumerate` vrací, je *iterátor dvojic* – sekvence, -jejíž prvky jsou dvojice. - -Sekvence `enumerate` toho „umí“ ještě míň než seznam nebo `range`. -Nejde z ní ani vybírat prvky, o metodách jako `append` ani nemluvě: - -```pycon ->>> enumerate(trpaslici)[3] -Traceback (most recent call last): - File "", line 1, in -TypeError: 'enumerate' object is not subscriptable -``` - -Když budeš od sekvence `enumerate` chtít něco víc než iterování, -převeď ji na seznam. - - -## Rozbalování v cyklu for - -Cyklus `for` umíš rozepsat: opakuje se v něm nastavení proměnné (které dělá -`for` za tebe), pak tělo cyklu, a znovu nastavení proměnné, tělo cyklu atd. -Pro „trpasličí“ cyklus to je: - -```python -dvojice = 0, 'Prófa' # nastavení proměnné dělá `for` -index, trpaslik = dvojice -print(f'Na pozici {index} je {trpaslik}!') - -dvojice = 1, 'Stydlín' # nastavení proměnné dělá `for` -index, trpaslik = dvojice -print(f'Na pozici {index} je {trpaslik}!') - -dvojice = 2, 'Dřímal' # nastavení proměnné dělá `for` -index, trpaslik = dvojice -print(f'Na pozici {index} je {trpaslik}!') - -# A tak dále -``` - -Kdybys to psal{{a}} ručně, lze to zjednodušit – přiřadit do dvou proměnných -najednou, bez pomocné proměnné `dvojice`: - -```python -index, trpaslik = 0, 'Prófa' # nastavení proměnných -print(f'Na pozici {index} je {trpaslik}!') - -index, trpaslik = 1, 'Stydlín' # nastavení proměnných -print(f'Na pozici {index} je {trpaslik}!') - -index, trpaslik = 2, 'Dřímal' # nastavení proměnných -print(f'Na pozici {index} je {trpaslik}!') - -# A tak dále -``` - -Cyklus `for` tohle ve skutečnosti umí: místo do proměnné `dvojice` může -přiřadit rovnou do dvou proměnných `index, trpaslik`: - -```python -for index, trpaslik in enumerate(trpaslici): - print(f'Na pozici {index} je {trpaslik}!') -``` - -Tohle je docela častý způsob práce s *iterátorem n-tic* – máš-li sekvenci, -jejíž prvky jsou n-tice, můžeš jednotlivé součásti n-tice -rozbalit přímo v hlavičce `for` cyklu. - -Zkus si to! Zkopíruj si tento seznam: - -```python -dny = ['Po', 'Út', 'St', 'Čt', 'Pá', 'So', 'Ne'] -``` - -… a zkus vypsat: - -```plain -1. Po -2. Út -3. St -4. Čt -5. Pá -6. So -7. Ne -``` - -{% filter solution %} - -```python -dny = ['Po', 'Út', 'St', 'Čt', 'Pá', 'So', 'Ne'] -for index, den in enumerate(dny): - cislo = index + 1 - print(f'{cislo}. {den}') -``` - -To je trošku kostrbaté, ale dá se to zjednodušit: buď jako -`f'{index + 1}. {den}'`, nebo můžeš funkci `enumerate` předat -pojmenovaný argument `start`, pomocí kterého umí sama -počítat od jiného začátku než od nuly: - -```python -dny = ['Po', 'Út', 'St', 'Čt', 'Pá', 'So', 'Ne'] -for index, den in enumerate(dny, start=1): - print(f'{index}. {den}') -``` - -{% endfilter %} - - -## Zip: Víc iterací najednou - -Další iterátor n-tic je funkce `zip`, která umí projít dvě sekvence -naráz. - -Řekněme, že máš seznam věcí a druhý seznam, ve kterém jsou barvy těch věcí: - -```python -veci = ['tráva', 'slunce', 'mrkev', 'list'] -barvy = ['zelená', 'žluté', 'oranžová', 'zelený'] -``` - -Když na ně zavoláš `zip`, dostaneš iterátor, který (podobně jako `enumerate` -nebo `range`) sám od sebe nic neříká: - -```pycon ->>> zip(veci, barvy) - -``` - -Po převedení na seznam se ale ukáže seznam odpovídajících si dvojic: - -* Dvojice prvních prvků obou seznamů: (`tráva`, `zelená`) -* Dvojice druhých prvků obou seznamů: (`slunce`, `žluté`) -* Dvojice třetích prvků obou seznamů: (`mrkev`, `oranžová`) -* A tak dál… - -```pycon ->>> list(zip(veci, barvy)) -[('tráva', 'zelená'), ('slunce', 'žluté'), ('mrkev', 'oranžová'), ('list', 'zelený')] -``` - -Takové dvojice jsou připravené na to, že je rozbalíš v cyklu `for`: - -``` python -for vec, barva in zip(veci, barvy): - print(f"{vec} je {barva}") -``` - -Funguje to i pro více sekvencí. -V následujícím případě vznikne iterátor čtveřic (věc, barva, -místo, číslo): - -```python -veci = ['tráva', 'slunce', 'mrkev', 'list'] -barvy = ['zelená', 'žluté', 'oranžová', 'zelený'] -mista = ['na zemi', 'nahoře', 'na talíři', 'na stromě'] -cisla = range(4) - -for vec, barva, misto, cislo in zip(veci, barvy, mista, cisla): - print(f"{cislo}. {barva} {vec} je {misto}") -``` - - -## Zip Longest: Pro ty co chtějí všechno - -Jak se `zip` chová, když dostane seznamy různých délek? - -```python -veci = ['tráva', 'slunce', 'mrkev', 'list', 'myšlenka', 'spravedlnost'] -barvy = ['zelená', 'žluté', 'oranžová', 'zelený'] -for vec, barva in zip(veci, barvy): - print(f"{vec} je {barva}") -``` - -{% filter solution %} -Iterátor `zip` skončí hned, když dojdou prvky nejkratší sekvence. -{% endfilter %} - -Občas je potřeba projít všechny záznamy. -Na to slouží funkce `zip_longest` z modulu `itertools`: - -```python -from itertools import zip_longest -for vec, barva in zip_longest(veci, barvy, fillvalue='(nevím)'): - print(f"{vec} je {barva}") -``` - -Pojmenovaný argument `fillvalue` říká, co se doplní za chybějící hodnoty. -Když ho nezadáš, doplní se `None` („nic“, hodnota kterou např. vrací procedury). -To se často používá, když je pro chybějící hodnoty potřeba nějaká -složitější logika: - -```python -from itertools import zip_longest -for vec, barva in zip_longest(veci, barvy): - if vec == None: - vec = 'nějaká věc' - if barva == None: - barva = 'bez barvy' - print(f"{vec} je {barva}") -``` diff --git a/lessons/beginners/zip-enumerate/info.yml b/lessons/beginners/zip-enumerate/info.yml deleted file mode 100644 index 566e2e99..00000000 --- a/lessons/beginners/zip-enumerate/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Iterátory n-tic -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. -license: cc-by-sa-40 diff --git a/lessons/git/branching/static/branch1.png b/lessons/branching/branch1.png similarity index 100% rename from lessons/git/branching/static/branch1.png rename to lessons/branching/branch1.png diff --git a/lessons/git/branching/static/branches.png b/lessons/branching/branches.png similarity index 100% rename from lessons/git/branching/static/branches.png rename to lessons/branching/branches.png diff --git a/lessons/branching/index.html b/lessons/branching/index.html new file mode 100644 index 00000000..5943df35 --- /dev/null +++ b/lessons/branching/index.html @@ -0,0 +1,180 @@ +

Větvení v Gitu +# +

+

Takže, Git už znáš! +Teď to začne být trošičku složitější :)

+

Programátoři občas potřebují pracovat na dvou +věcech zároveň. +V projektu do práce se objeví se chyba, +která musí být opravená +ještě dnes, tak programátor/ka opustí, co zrovna dělá, +vrátí se k nějaké „stabilní” verzi, opraví chybu +a odešle ji zákazníkům. +A pak se vrátí k tomu, co dělal/a předtím – jen ještě +musí zakomponovat opravu chyby i do verze, na které +pracuje dlouhodobě.

+

Git na to má takzvané větve (angl. branches). +Na jedné „větvi” se pracuje, ale je možné se přepnout do +jiné (třeba starší) větve, udělat pár změn +a pak se zase přepnout do nové větve a +pokračovat dál nebo sloučit změny.

+

Větvení využijeme i při spolupráci více lidí – každý +dělá na vlastní větvi a když přijde čas, +tak se různé změny sloučí dohromady.

+

Podívej se, jaké máš větve ve svém repozitáři. +K tomu slouží příkaz git branch:

+
$ git branch
+* master
+

Je tam jenom jedna a jmenuje se master +– to je tradičně jméno „hlavní” větve.

+

K vytvoření nové větve znovu použiješ +git branch, jen tomu příkazu dáš navíc +jméno nové větve. +Třeba budeš chtít k básničce doplnit jméno autora, +tak větev pojmenuješ doplneni-autora.

+
$ git branch doplneni-autora
+$ git branch
+  doplneni-autora
+* master
+

Tenhle příkaz sice udělal novou větev, +ale nepřepnul do ní. +Hvězdička ve výstupu z git branch ukazuje, +že stále pracuješ v master. +Na přepnutí budeš potřebovat další příkaz:

+
$ git checkout doplneni-autora
+Switched to branch 'doplneni-autora'
+$ git branch
+* doplneni-autora
+  master
+

Tak. Teď jsi „ve” větvi doplneni-autora. +Doplň nějaké jméno na začátek souboru basnicka.txt, +a pomocí git add a git commit udělej novou revizi. +Pak koukni na gitk --all, jak to vypadá:

+

Výstup programu `gitk` s větví doplneni-autora

+

Aktuální větev – doplneni-autora – je +zvýrazněná tučně a starší master je stále +na původní revizi.

+

Opusťme teď na chvíli práci na doplňování autora. +Vrať se do větve master a vytvoř z ní +větev doplneni-jmena. +Pak se na tuhle novou větev přepni.

+
$ git checkout master
+Switched to branch 'master'
+$ git branch doplneni-jmena
+$ git checkout doplneni-jmena
+Switched to branch 'doplneni-jmena'
+$ git branch
+  doplneni-autora
+* doplneni-jmena
+  master
+

Doplň jméno básně na začátek souboru (tedy na stejné místo, +jako je v druhé větvi název) a pomocí +git add, git commit ulož revizi. +Všechno zkontroluj přes gitk --all.

+

Výstup programu `gitk` s větvemi doplneni-autora a doplneni-nazvu

+

Takhle nějak se dá postupovat v situaci popsané v úvodu: +opuštění rozpracované verze, přechod na „stabilní” +verzi master a začátek práce v jiné +části projektu.

+

Mezi jednotlivými větvemi se dá podle libosti přepínat, +jen je vždycky dobré před přepnutím udělat novou revizi +(git commit) a pomocí git status zkontrolovat, jestli je všechno +uložené v Gitu.

+

Na stejném principu funguje i spolupráce několika lidí +na jednom projektu: je nějaký společný základ +(master) a každý dělá na vlastní větvi, dokud není se svými změnami spokojený.

+

A až je některá větev hotová, může se začlenit +zpátky do master. Podívejme se jak na to.

+

Sloučení +# +

+

Nedávalo by smysl historii projektu rozdvojovat, +kdyby pak jednotlivé větve nešly zase sloučit dohromady. +V Gitu je většinou slučování poměrně jednoduché, ale tento příklad schválně +ukazuje nejsložitější variantu, která může nastat.

+

Přepni se zpátky na master +a použij příkaz git merge, který +sloučí jinou větev s tou aktuální. +Příkazu musíš dát jméno větve, kterou chceš sloučit.

+
$ git checkout master
+Switched to branch 'master'
+$ git merge doplneni-jmena
+Updating 1fcd654..5c9bf93
+Fast-forward
+ basnicka.txt | 3 +++
+ 1 file changed, 3 insertions(+)
+

Sloučeno! Ono „Fast-forward” znamená, že +vlastně nebylo co slučovat – jen se do větve +master přidaly nové změny. +Zkontroluj v gitk --all, jak to vypadá.

+

A pak zkus sloučit i druhou větev: git merge doplneni-autora. +Tady to bude složitější: pravděpodobně se stane, že změny nepůjdou +automaticky sloučit a ve výstupu se objeví hláška +merge conflict (slučovací konflikt):

+
$ git merge doplneni-autora
+Auto-merging basnicka.txt
+CONFLICT (content): Merge conflict in basnicka.txt
+Automatic merge failed; fix conflicts and then commit the result.
+

A když ne?

+

Jestli se konflikt neobjevil, Git změny sloučil sám. +Gratuluji! Zbytek téhle sekce bude jen teoretický; vrať se sem, až se ti +někdy „podaří“ konflikt udělat.

+

Když nastane konflikt, git status ukáže „both modified“ – tedy že byl soubor +změněný v obou slučovaných větvích:

+
$ git status
+On branch master
+You have unmerged paths.
+  (fix conflicts and run "git commit")
+  (use "git merge --abort" to abort the merge)
+
+Unmerged paths:
+  (use "git add <file>..." to mark resolution)
+	both modified:   basnicka.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+

V tom případě se na soubor podívej v editoru: objeví +se v něm obsah z obou konfliktních verzí, +společně se značkami <<<<<<<, ======= a >>>>>>>, které upozorňují na +místo kde konflikt nastal.

+

Značky ukáže i příkaz git diff, jehož výstup je teď trošku složitější:

+
$ git diff
+diff --cc basnicka.txt
+index bc7a2de,88e7ea5..0000000
+--- a/basnicka.txt
++++ b/basnicka.txt
+@@@ -1,4 -1,4 +1,8 @@@
+++<<<<<<< HEAD
+ +Holka Modrooká
+++=======
++ (Lidová)
+++>>>>>>> doplneni-autora
+  
+  Holka modrooká
+  Nesedávej u potoka
+

Konflikty a značky, které můžou být nepřehledné, ukazuje Git ze stejného důvodu +jako Python chybové hlášky: tedy aby ti co nejvíce pomohl. +Git je jen „hloupý“ nástroj a s konfliktem si neporadí sám, ale snaží se +ti řešení konfliktu co nejvíc usnadnit.

+

Proto hlavně nepanikař! +Soubor otevři v editoru a uprav tak, jak by měl vypadat. +Značky jako <<<<<<< smaž; řádky poskládej tak, jak by měly jít za sebou. +(A pracuješ-li na kódu, spusť testy a ověř, jestli všechno stále funguje.)

+

Pak soubor ulož a zadej git commit.

+

Ať nastal konflikt nebo ne, vytvoří se „slučovací revize“ +(angl. merge commit), které – jako každé revizi – můžeš dát popisek. +Tentokrát je popisek už předvyplněný; chceš-li nějaký jiný, nahraď ho.

+
$ git add basnicka.txt
+$ git commit
+[master 884b30a] Merge branch 'doplneni-autora'
+

Povedlo se?

+

Výstup programu `gitk` s větvemi doplneni-autora a doplneni-nazvu sloučenými do master

+

Pokud ano, můžeš staré větve vymazat – všechny jejich +změny jsou v master a nemá na nich cenu +pracovat dál.

+
$ git branch --delete doplneni-autora
+Deleted branch doplneni-autora (was f1cd9be).
+$ git branch --delete doplneni-jmena
+Deleted branch doplneni-jmena (was 5c9bf93).
+$ git branch
+* master
+

Gratuluji, už umíš větvení a slučování!

\ No newline at end of file diff --git a/lessons/git/branching/static/merge.png b/lessons/branching/merge.png similarity index 100% rename from lessons/git/branching/static/merge.png rename to lessons/branching/merge.png diff --git a/lessons/beginners/circular-imports/index.md b/lessons/circular-imports/index.html similarity index 56% rename from lessons/beginners/circular-imports/index.md rename to lessons/circular-imports/index.html index 2947075e..9205353b 100644 --- a/lessons/beginners/circular-imports/index.md +++ b/lessons/circular-imports/index.html @@ -1,14 +1,12 @@ -## Cyklické importy - -V domácích úkolech budeš rozdělovat piškvorkový projekt na několik modulů. +

Cyklické importy +# +

+

V domácích úkolech budeš rozdělovat piškvorkový projekt na několik modulů. Tento text si doporučuju číst až když narazíš na příslušný úkol, -abys věděl{{a}} o čem tu je řeč. - -Po rozdělení bude projekt vypadat třeba nějak takhle: -(Šipky mezi moduly znázorňují importování.) - -```plain -┌──────────────────╮ ┌───────────────╮ ┌──────────────────╮ +abys věděl/a o čem tu je řeč.

+

Po rozdělení bude projekt vypadat třeba nějak takhle: +(Šipky mezi moduly znázorňují importování.)

+
┌──────────────────╮  ┌───────────────╮  ┌──────────────────╮
 │      ai.py       │  │ piskvorky.py  │  │    hra.py        │
 ├──────────────────┤  ├───────────────┤  ├──────────────────┤
 │                  │◀-│ import ai     │◀-│ import piskvorky │
@@ -18,16 +16,12 @@
 └──────────────────┘  │ def tah_hrace │  └──────────────────┘
                       │               │
                       └───────────────┘
-```
-
-Jenže funkce `tah_pocitace`
-většinou potřebuje volat funkci `tah`.
+

Jenže funkce tah_pocitace +většinou potřebuje volat funkci tah. Co s tím? -Můžeš importovat `ai` z `piskvorky` a zároveň -`piskvorky` z `ai`? - -```plain -┌──────────────────╮ ┌───────────────╮ +Můžeš importovat aipiskvorky a zároveň +piskvorkyai?

+
┌──────────────────╮  ┌───────────────╮
 │      ai.py       │  │ piskvorky.py  │
 ├──────────────────┤  ├───────────────┤
 │                  │◀-│ import ai     │
@@ -38,52 +32,43 @@
 └──────────────────┘  │ def tah_hrace │
                       │               │
                       └───────────────┘
-```
-
-Můžeš se na to podívat z pohledu Pythonu,
+

Můžeš se na to podívat z pohledu Pythonu, který příkazy v souborech vykonává. -Když má importovat soubor `piskvorky.py`, začne ho +Když má importovat soubor piskvorky.py, začne ho zpracovávat řádek po řádku, -když tu (docela brzo) narazí na příkaz `import ai`. -Otevře tedy soubor `ai.py` +když tu (docela brzo) narazí na příkaz import ai. +Otevře tedy soubor ai.py a začne ho zpracovávat řádek po řádku. -Brzy narazí na příkaz `import piskvorky`. Co teď? - -Aby nenastala situace podobná nekonečné smyčce – +Brzy narazí na příkaz import piskvorky. Co teď?

+

Aby nenastala situace podobná nekonečné smyčce – jeden soubor by importoval druhý, druhý zase první, a tak stále dokola – udělá Python takový malý „podvod“: -když zjistí, že soubor `piskvorky.py` -už importuje, zpřístupní v modulu `ai` -modul `piskvorky` tak, jak ho +když zjistí, že soubor piskvorky.py +už importuje, zpřístupní v modulu ai +modul piskvorky tak, jak ho má: nekompletní, bez většiny funkcí co v něm mají být nadefinované. -A až potom, co dokončí import `ai.py`, -se vrátí k souboru `piskvorky.py` -a pokračuje v provádění příkazů `def`, které v něm jsou. +A až potom, co dokončí import ai.py, +se vrátí k souboru piskvorky.py +a pokračuje v provádění příkazů def, které v něm jsou. Takový nekompletní modul může být občas užitečný, ale ve většině případů se chová skoro -nepředvídatelně a tudíž nebezpečně. - -Jinými slovy: když se dva moduly importují navzájem, -nemusí to fungovat podle očekávání. - -Téhle situaci se budeš chtít vyvarovat. - -Jak na to? Máš dvě možnosti. - - -## Organizace modulů podle závislostí - -První možnost je definovat funkci `tah` v modulu `ai` +nepředvídatelně a tudíž nebezpečně.

+

Jinými slovy: když se dva moduly importují navzájem, +nemusí to fungovat podle očekávání.

+

Téhle situaci se budeš chtít vyvarovat.

+

Jak na to? Máš dvě možnosti.

+

Organizace modulů podle závislostí +# +

+

První možnost je definovat funkci tah v modulu ai a používat ji odtamtud. To je jednoduché, ale nerespektuje účel modulu -`ai`, který má obsahovat jenom logiku +ai, který má obsahovat jenom logiku vybírání tahu počítače, a ne pomocné funkce, které -můžou být potřeba i jinde. - -```plain -┌──────────────────╮ ┌───────────────╮ +můžou být potřeba i jinde.

+
┌──────────────────╮  ┌───────────────╮
 │      ai.py       │  │ piskvorky.py  │
 ├──────────────────┤  ├───────────────┤
 │                  │◀-│ import ai     │
@@ -92,18 +77,14 @@
 │ def tah          │  │ def tah_hrace │
 │                  │  │               │
 └──────────────────┘  └───────────────┘
-```
-
-## Pomocný modul
-
-Druhá možnost je definovat nový, sdílený modul,
-který se použije jak v `piskvorky.py` tak v `ai.py`.
-
-Takový modul se často pojmenovává
-`util.py` (z angl. *utility*, pomůcka, nástroj).
-
-```plain
-              ┌──────────────────╮
+

Pomocný modul +# +

+

Druhá možnost je definovat nový, sdílený modul, +který se použije jak v piskvorky.py tak v ai.py.

+

Takový modul se často pojmenovává +util.py (z angl. utility, pomůcka, nástroj).

+
              ┌──────────────────╮
               │ util.py          │
               ├──────────────────┤
               │ def tah          │
@@ -120,13 +101,10 @@
 │                  │        │ def tah_hrace │
 │                  │        │               │
 └──────────────────┘        └───────────────┘
-```
-
-Nevýhoda pomocného modulu je ta,
+

Nevýhoda pomocného modulu je ta, že se z něj může stát neudržované „odkladiště“ všeho kódu, který byl jednou potřeba na dvou -nebo více místech. - -Pro kterou z možností se rozhodnout, záleží +nebo více místech.

+

Pro kterou z možností se rozhodnout, záleží na situaci. -Programování není vždycky jen exaktní věda! +Programování není vždycky jen exaktní věda!

\ No newline at end of file diff --git a/lessons/class/index.html b/lessons/class/index.html new file mode 100644 index 00000000..5e85d80b --- /dev/null +++ b/lessons/class/index.html @@ -0,0 +1,270 @@ +

Hodnoty a objekty +# +

+

Než se dnes začneme zabývat třídami, +podíváme na objekty.

+

Co pro programátory znamená slovo objekt?

+

V Pythonu je to jednoduché – každá hodnota +(tj. něco, co můžeš uložit do proměnné, vrátit +z funkce nebo třeba seznamu) je objekt. +Některé jazyky (třeba Javascript, C++ nebo Java) mají +i jiné hodnoty než objekty, v některých +jazycích (třeba v C) objekty vůbec nejsou. +Ale v Pythonu mezi hodnotou a objektem není rozdíl, +takže je na jednu stranu trošku složitější pochopit, +v čem spočívá ta „objektovitost“, ale na druhou stranu +to zase není potřeba vědět do detailů.

+

Základní vlastnost objektů je to, že obsahují jak data +(informace), tak chování – instrukce nebo metody, +které s těmito daty pracují. +Třeba řetězce v Pythonu obsahují jak informace +(nějakou sekvenci znaků), tak užitečné metody jako +upper nebo count. +Kdyby řetězce nebyly objekty, musel by Python mít +spoustu funkcí jako str_upper a str_count. +Objekty spojují data a funkčnost dohromady.

+

Možná namítneš, že třeba len je funkce. +Je to tak, Python není „stoprocentně“ objektový jazyk. +Funkce len ale funguje i na +objektech, které s řetězci nemají nic společného.

+

Třídy +# +

+

Data každého objektu jsou specifická pro konkrétní +objekt ("abc" obsahuje jiné znaky než +"def"), ale funkčnost – metody – bývají +společné pro všechny objekty daného typu. +Třeba řetězcová metoda count() by se dala +napsat zhruba jako:

+
def count(retezec, znak):
+    pocet = 0
+    for c in retezec:
+        if c == znak:
+            pocet = pocet + 1
+    return pocet
+

… a ačkoliv bude vracet jinou hodnotu pro každý řetězec, +samotná metoda je společná všem řetězcům.

+

Tohle společné chování určuje +typ (angl. type) neboli třída (angl. class) daného objektu.

+

V jiných jazycích než Python 3 můžou slova „typ“ a „třída“ označovat různé +věci. Pro nás to budou synonyma.

+

Typ objektu umí zjistit funkce type:

+
>>> type(0)
+<class 'int'>
+>>> type(True)
+<class 'bool'>
+>>> type("abc")
+<class 'str'>
+>>> with open('soubor.txt') as f:
+...     type(f)
+... 
+<class '_io.TextIOWrapper'>
+

A co je to ten typ neboli třída? Je to popis, jak se všechny objekty +daného typu chovají.

+

Například <class 'int'> obsahuje všechno, co je společné všem celým číslům: +že (a jak) se dají sčítat, jak takové číslo převést na řetězec, a tak dále.

+

Tvoření objektů třídy +# +

+

Většinu tříd jde navíc v Pythonu zavolat, jako by +to byly funkce, a vytvořit tak nový objekt dané třídy:

+
>>> trida_retezcu = type("abc")
+>>> trida_retezcu(8)
+'8'
+>>> trida_retezcu([1, 2, 3])
+'[1, 2, 3]'
+

Chová se to stejně jako funkce str! Není to podivné?

+

Tady se musím omluvit: +materiály k funkcím +tak trochu lhaly. Funkce str, int, float apod. totiž vůbec +nejsou funkce – jsou to právě třídy:

+
>>> str
+<class 'str'>
+>>> type('abcdefgh')
+<class 'str'>
+>>> type('abcdefgh') == str
+True
+

Ale dají se, podobně jako funkce, zavolat. +Třída tedy většinou obsahuje nejen „popis“, jak se +její objekty budou chovat, ale „umí“ takové objekty i vytvořit.

+

Vlastní třídy +# +

+

A proč najednou tolik informací o třídách? +Protože si zkusíme napsat třídu vlastní.

+

Třídu se hodí napsat, když plánuješ mít ve svém +programu více objektů s podobným chováním. +Třeba karetní hra by mohla mít třídu Karta, +webová aplikace třídu Uživatel, +tabulkový procesor třídu Řádek.

+

My teď potřebujeme napsat program o zvířátkách. +Začni tím, že napíšeš třídu pro koťátka, která umí mňoukat:

+
class Kotatko:
+    def zamnoukej(self):
+        print("Mňau!")
+

Tak jako se funkce definují pomocí def, +třídy mají klíčové slovo class, +za které napíšeš jméno třídy, dvojtečku a pak odsazené tělo třídy. +Podobně jako def dělá funkce, příkaz +class udělá novou třídu a přiřadí ji +do proměnné daného jména (tady Kotatko).

+

Třídy se tradičně pojmenovávají s velkým písmenem, +aby se nepletly s „normálními“ hodnotami.

+

Základní třídy (str, int atd.) +velká písmena nemají, a to hlavně z historických +důvodů – původně to byly opravdu funkce.

+

V těle třídy můžeš definovat metody, které vypadají +úplně jako funkce – jen mají první parametr self. +Ten si ale vysvětlíme později – napřed zkus zamňoukat:

+
# Vytvoření konkrétního objektu
+mourek = Kotatko()
+
+# Volání metody
+mourek.zamnoukej()
+

V tomhle příkladu si dej pozor na velikost písmen: +Kotatko (s velkým K) je třída – popis, jak +se koťátka chovají. +mourek (s malým m) +je konkrétní objekt (angl. instance) té třídy: +hodnota, která reprezentuje kotě.

+

Když definuješ třídu (pomocí bloku class), neznamená to zatím, že ve tvém +programu je nějaké koťátko. +Třída je jako recept nebo manuál: když si koupíš kuchařku, budeš teoreticky +vědět jak upéct dort, jak bude takový dort vypadat a že se dá sníst. +Ale neznamená to ještě, že máš samotný dort!

+

Konkrétní objekt vytvoříš až zavoláním třídy. +Stejně jako zavoláním str() se dá vytvořit konkrétní řetězec, +volání Kotatko() vytvoří nový objekt tvé třídy, který už můžeš použít.

+

Mňau!

+

Atributy +# +

+

Objekty vytvořené z „vlastních“ tříd mají funkčnost, kterou třídy jako str +nedovolují: máš možnost si definovat vlastní +atributy – informace, které se uloží k danému objektu. +Atributy se označují tak, že mezi hodnotu a jméno +jejího atributu napíšeš tečku:

+
mourek = Kotatko()
+mourek.jmeno = 'Mourek'
+
+micka = Kotatko()
+micka.jmeno = 'Micka'
+
+print(mourek.jmeno)
+print(micka.jmeno)
+

Na začátku jsme si řekli, že objekty spojují chování +a data. +Chování je definováno ve třídě; data se schovávají +právě v atributech jednotlivých objektů. +Podle atributů jako jmeno pak můžeš jednotlivá koťátka +rozlišit.

+

Asi sis všiml/a, že tečkou se dostaneš jak k metodám +převzaným z třídy, tak k atributům specifickým +pro konkrétní objekt. +Co se stane, když má atribut stejné jméno jako +metoda z třídy? Vyzkoušej si to:

+
micka = Kotatko()
+micka.zamnoukej = 12345
+micka.zamnoukej()
+

Parametr self +# +

+

Teď se na chvíli vraťme k metodám. Konkrétně k parametru self.

+

Každá metoda má přístup ke konkrétnímu objektu, na +kterém pracuje, právě přes parametr self. +Teď, když máš koťátka pojmenovaná, můžeš v metodě zamnoukej použít self +a dostat se tak ke jménu daného koťátka:

+
class Kotatko:
+    def zamnoukej(self):
+        print(f"{self.jmeno}: Mňau!")
+
+mourek = Kotatko()
+mourek.jmeno = 'Mourek'
+
+micka = Kotatko()
+micka.jmeno = 'Micka'
+
+mourek.zamnoukej()
+micka.zamnoukej()
+

Co se stalo? Výraz mourek.zamnoukej udělá metodu. +Když ji pak zavoláš (mourek.zamnoukej()), +objekt mourek se předá funkci zamnoukej jako první argument, self.

+

Onen první parametr metody můžeš teoreticky pojmenovat i jinak než self, +ale když to uděláš, ostatní programátoři se na tebe budou koukat hodně divně.

+

Metoda může mít po self i další parametry. +Při volání self vynecháš – doplní se vždy automaticky – ale ostatní +se předávají jako u normálního volání funkce. +Třeba v tomto příkladu se jako jidlo předá řetězec 'ryba':

+
class Kotatko:
+    def zamnoukej(self):
+        print(f"{self.jmeno}: Mňau!")
+
+    def snez(self, jidlo):
+        print(f"{self.jmeno}: Mňau mňau! {jidlo} mi chutná!")
+
+mourek = Kotatko()
+mourek.jmeno = 'Mourek'
+mourek.snez('ryba')
+

Metoda __init__ +# +

+

Co se stane, když koťátku zapomeneš nastavit jméno? +Metoda zamnoukej přestane fungovat:

+
>>> micka = Kotatko()
+>>> micka.snez('ryba')
+Traceback (most recent call last):
+  File "<zvirata.py>", line 5, in snez
+AttributeError: 'Kotatko' object has no attribute 'jmeno'
+

Aby tahle chyba nemohla nastat, můžeš zařídit, aby každé kotě muselo být +pojmenované už od okamžiku, kdy vznikne. +Jméno pak budeš zadávat už při vytváření kotěte, nějak takhle:

+
mourek = Kotatko(jmeno='Mourek')
+

To ale zatím nefunguje; musíš na to třídu Kotatko připravit.

+

Použij na to speciální metodu, která se jmenuje __init__ (dvě podtržítka, +init, dvě podtržítka). +To „opodtržítkování“ znamená, že tohle jméno je nějakým způsobem speciální. +Metodu __init__ totiž Python zavolá +automaticky, když vytvoří nový objekt. +Můžeš tedy napsat:

+
class Kotatko:
+    def __init__(self, jmeno):
+        self.jmeno = jmeno
+
+    def zamnoukej(self):
+        print(f"{self.jmeno}: Mňau!")
+
+    def snez(self, jidlo):
+        print(f"{self.jmeno}: Mňau mňau! {jidlo} mi chutná!")
+
+mourek = Kotatko('Mourek')
+mourek.zamnoukej()
+

A teď už není možnost jak vytvořit koťátko beze jména. +Metoda zamnoukej bude vždycky fungovat.

+

Jako u jiných funkcí je možné jméno koťátka zadat buď jako pojmenovaný +argument, nebo jako poziční. Obojí funguje stejně:

+
mourek = Kotatko('Mourek')  # 'Mourek' je hodnota prvního argumentu pro __init__ (po self)
+micka = Kotatko(jmeno='Micka')  # 'Micka' je hodnota argumentu `jmeno`
+

Metoda __str__ +# +

+

Podobných „opodtržítkovaných“ (speciálních) metod je víc. +Třeba metodu __str__ Python zavolá, když je potřeba +převést objekt na řetězec:

+
class Kotatko:
+    def __init__(self, jmeno):
+        self.jmeno = jmeno
+
+    def __str__(self):
+        return f'<Kotatko jmenem {self.jmeno}>'
+
+    def zamnoukej(self):
+        print(f"{self.jmeno}: Mňau!")
+
+    def snez(self, jidlo):
+        print(f"{self.jmeno}: Mňau mňau! {jidlo} mi chutná!")
+
+mourek = Kotatko('Mourek')
+print(mourek)
+

A to je o samotných třídách zatím vše. +Příště si něco řekneme o dědičnosti. +A o štěňátkách.

\ No newline at end of file diff --git a/lessons/click/index.html b/lessons/click/index.html new file mode 100644 index 00000000..7c1462f9 --- /dev/null +++ b/lessons/click/index.html @@ -0,0 +1,203 @@ +

click +# +

+

Knihovna click slouží k vytváření rozhraní pro příkazovou řádku +(angl. command line interface, CLI). +Primárně to je zpracování argumentů, ale click umí zjednodušit i výstup.

+

Click je dobré používat s knihovnou colorama, která se stará o obarvování +textu na příkazové řádce ve Windows (a na Unixu nedělá nic). +Nainstalujte si tedy obě:

+
$ python -m pip install click colorama
+

Argumenty příkazové řádky +# +

+

Nástroje na zpracování argumentů z CLI jsou i přímo ve standardní knihovně, +a dokonce jich není málo: sys.argv, argparse, optparse, getopt. +S knihovnou click je ale práce mnohem příjemnější a výsledky většinou +lépe odpovídají zavedeným konvencím.

+

Cena za jednoduchost a konzistenci je, že některé styly návrhu CLI click +nepodporuje. +Máte-li existující rozhraní, které chcete jen převést do Pythonu, +click nejspíš nebude nejlepší volba.

+

Takto jednoduše se dá vytvořit aplikace s přepínači:

+
import click
+
+@click.command()
+@click.option('--count', default=1,  metavar='COUNT',
+              help='Number of greetings.')
+@click.option('--name', prompt='Your name', metavar='NAME',
+              help='The person to greet.')
+def hello(count, name):
+    """Simple program that greets NAME for a total of COUNT times."""
+    for x in range(count):
+        click.echo(f'Hello {name}!')
+
+if __name__ == '__main__':
+    hello()
+

Vyzkoušejte si ji! Máte-li ji uloženou jako hello.py, zkuste:

+
$ python hello.py
+$ python hello.py --help
+$ python hello.py --name Pythonista
+$ python hello.py --count 5
+

Příkazy a přepínače +# +

+

Funkce s dekorátorem @click.command je příkaz – když ji zavoláte, +click zpracuje argumenty příkazové řádky a zavolá původní funkci +s příslušnými pythonními hodnotami. +Proto se dává do bloku if __name__ == '__main__':, který se spustí, jen +když se pythonní soubor spoustí „přímo“. +Když je soubor importován, tenhle blok se neprovede.

+

Dekorátory @click.option a @click.argument pak přidávají přepínače +a argumenty.

+

Přepínače (angl. options), přidávané pomocí option, jsou nepovinné +parametry, kterými se nějak obměňuje chování programu. +Pokud uživatel nějaký přepínač nezadá, použije se hodnota zadaná jako default +(nebo None, když default chybí).

+

Podle default se řídí i typ argumentu, není-li dán explicitně. +Takže count v příkladu výše je celé číslo.

+

Jména přepínačů začínají, podle Unixové konvence, pomlčkami: jednou pomlčkou +pro jednopísmenné zkratky, dvěma pomlčkami pro vícepísmenná jména. +Jeden přepínač může mít i víc jmen.

+

Speciální případ jsou booleovské přepínače, které mají jedno jméno +pro True a jiné pro False. Lze samozřejmě také vytvořit bezhodnotový +přepínač pomocí is_flag.

+
import click
+
+@click.command()
+@click.option('-n', '--name', default='world',
+              help='Name of the person to greet')
+@click.option('-c/-C', '--color/--no-color',
+              help='Make the output colorful')
+@click.option('-v', '--verbose', is_flag=True,
+              help='More verbose output')
+def hello(name, color, verbose):
+    if color:
+        name = click.style(name, fg='blue')
+    click.echo(f'Hello {name}!')
+    if verbose:
+        click.echo('Nice to meet you.')
+
+if __name__ == '__main__':
+    hello()
+
$ python hello.py
+Hello world!
+$ python hello.py --name Guido
+Hello Guido!
+$ python hello.py --name Jane -v
+Hello Jane!
+Nice to meet you.
+$ python hello.py -n 'Mr. Git'
+Hello Mr. Git!
+$ python hello.py --help
+Usage: hello.py [OPTIONS]
+
+Options:
+  -n, --name TEXT               Name of the person to greet
+  -c, --color / -C, --no-color  Make the output colorful
+  --help                        Show this message and exit.
+

Přepínač --help přidává click sám.

+

Argumenty +# +

+

Kromě přepínačů podporuje click i argumenty. +Přepínače musí uživatel na řádce pojmenovat; argumenty se zadávají beze jména, +ale záleží u nich na pořadí. +Používají se ve dvou případech: pro povinné parametry a pro parametry, kterých +může být zadán libovolný počet. +Na všechno ostatní radši použijte přepínače.

+

Například příkaz cd potřebuje jeden argument: jméno adresáře, +do kterého má přepnout. +Jeho rozhraní by v clicku vypadalo takto:

+
@click.command()
+@click.argument('directory')
+def cd(directory):
+    """Change the current directory"""
+    click.echo(f'Changing to directory {directory}')
+

Proměnný počet argumentů se zadává pomocí nargs=-1 (0 nebo víc argumentů) +nebo nargs=-1, required=True (1 nebo víc).

+

Například příkaz mv bere N souborů a adresář, kam je přesune. +Takové rozhraní by v clicku vypadalo následovně:

+
@click.command()
+@click.argument('source', nargs=-1, required=True)
+@click.argument('destination')
+def mv(source, destination):
+    """Move any number of files to one destination"""
+    for filename in source:
+        click.echo(f'Moving {filename} to {destination}')
+

Soubory +# +

+

Má-li uživatel zadat jméno souboru, nepoužívejte řetězce, ale speciální typ +click.File(). +Click za vás soubor automaticky otevře a zavře. +Kromě toho podporuje unixovskou konvenci, že - znamená standardní +vstup/výstup.

+

Argument pro File je mód, ve kterém se soubor otevírá, podobně jako pro +funkci open: +'r' pro čtení, 'w' pro zápis.

+
@click.command()
+@click.argument('files', nargs=-1, type=click.File('r'))
+def cat(files):
+    """Print out the contents of the given files"""
+    for file in files:
+        print(file.read(), end='')
+

Existuje i varianta click.Path(), +která soubor neotvírá. Pomocí ní jde např. zadat jméno adresáře. Click takto +poskytuje i jiné další typy.

+

Validace vstupů +# +

+

Vstupy získané z přepínačů i argumentů lze ověřit pomocí +vlastních podmínek a podle toho naprogramovat chování včetně +chybových hlášek. Click však opět nabízí pohodlnější způsob, +a to pomocí callback. +V rámci callback funkce můžete ověřit libovolně hodnotu a/nebo +ji vhodně transformovat. Pokud hodnota neodpovídá požadavkům, +můžete použít vyjímku click.UsageError +nebo click.BadParameter +(vztahuje-li se přímo ke konkrétnímu parametru). Click se pak +sám postará o případné ukončení programu s odpovídající chybovou +hláškou a kódem.

+
def validate_username(ctx, param, value):
+    if 2 <= len(value) <= 8 and re.match('^[a-zA-Z]+[0-9]*$', value):
+        return value.lower()
+    else:
+        raise click.BadParameter('not valid CTU username')
+
+@click.command()
+@click.option('-u', '--username', callback=validate_username)
+def email(username):
+    click.echo(f'{username}@fit.cvut.cz')
+
+if __name__ == '__main__':
+    email()
+

Podpříkazy +# +

+

Click má dobrou podporu pro podpříkazy známé z verzovacích systémů jako git: +příkaz git sám o sobě nedělá nic, jen sdružuje podpříkazy jako git add +a git commit.

+

Umí-li váš program více akcí, souhrnný příkaz označte @click.group() +a jednotlivé podpříkazy pak přidávejte pomocí command():

+
@click.group()
+def git2():
+    pass
+
+@git2.command()
+def commit():
+    message = click.edit('Made some changes')
+    click.echo(f'Making commit with message: {message}')
+
+@git2.command()
+@click.argument('files', nargs=-1)
+def add(files):
+    for file in files:
+        click.echo(f'Adding {file}')
+

A další +# +

+

Tahle lekce není popis všeho, co click umí – je to jen ochutnávka, +abyste věděli, co od téhle knihovny očekávat.

+

Click má velice dobrou dokumentaci, ve které najdete detaily i všechny +ostatní možnosti.

\ No newline at end of file diff --git a/lessons/cmdline/index.html b/lessons/cmdline/index.html new file mode 100644 index 00000000..50f1158d --- /dev/null +++ b/lessons/cmdline/index.html @@ -0,0 +1,359 @@ +

Příkazová řádka +# +

+

V této lekci se seznámíme s příkazovou řádkou – černým okýnkem, +které programátoři používají na zadávání textových příkazů.

+

Na většinu z toho, co příkazová řádka umí, můžeš použít i něco jiného – ikonku +na ploše, speciální program nebo editor, webovou aplikaci – ale tyhle +vychytávky mají dvě nevýhody:

+
    +
  • často se liší mezi různými počítači, takže s „tvojí“ variantou ti bude moci +pomoct míň lidí, a
  • +
  • z příkazové řádky se dá jednoduše kopírovat text, což zjednodušuje +spolupráci přes e-mail nebo chat.
  • +
+

I když příkazová řádka možná není úplně nejjednodušší způsob jak +s programováním začít, +dlouhodobě se ti základy práce s ní určitě vyplatí. +A i o tom tenhle kurz je.

+

Příkazová řádka (respektive program, kterému se říká i konzole či terminál; +anglicky command line, console, terminal) +se na různých systémech otevírá různě:

+
    +
  • Windows (české): Start → napsat na klávesnici „cmd“ → Příkazový řádek
  • +
  • Windows (anglické): Start → napsat na klávesnici „cmd“ → Command Prompt
  • +
  • macOS (anglický): Applications → Utilities → Terminal
  • +
  • Linux (GNOME, čeština): Činnosti → hledat Terminál
  • +
  • Linux (GNOME, angličtina): Activities → hledat Terminál
  • +
  • Linux (KDE): Hlavní Menu → hledat Konsole
  • +
+

Nevíš-li si rady, zkus buď googlit, nebo se zeptat někoho zkušenějšího.

+

Po otevření konzole tě uvítá výzva (angl. prompt): řádek, +kterým počítač vybízí k zadání příkazu. +Výzva končí na Unixových systémech (např. Linux a macOS) znakem $; +na Windows znakem >.

+

Před tím znakem $ nebo > budou nejspíš ještě nějaké další +informace, které jsou ovšem v těchto materiálech vynechané. +A budou vynechané i ve většině ostatních návodů co najdeš na internetu. +Na každém počítači totiž můžou být trochu jiné.

+
+

Unix (Linux, macOS)

$
+
+

Windows

>
+
+ +

Podle systému se potom liší i samotné příkazy, které budeš zadávat: +Unixové systémy (Linux a macOS) rozumí jiným příkazům než Windows.

+

Velikost písma

+

Je-li ve Windows moc malé písmo, klikni na ikonku okna a vyber Možnosti. +V záložce Písmo si pak můžeš vybrat větší font.

+ + +

Screenshot menu příkazové řádky

+

Na ostatních systémech hledej v nastavení, nebo zkus +Ctrl++ a +Ctrl+- (příp. se Shift).

+

První příkaz +# +

+

Začněme ale příkazem, který je všude stejný. +Napiš whoami (z angl. who am I? – kdo jsem?) +a stiskni Enter. +Objeví se přihlašovací jméno. Třeba u Heleny by to vypadalo takhle:

+
+

Unix

$ whoami
+helena
+
+

Windows

> whoami
+pocitac\Helena
+
+ +

Znak $ nebo > je v ukázce jen proto, aby bylo jasné že zadáváš +příkaz do příkazové řádky. +Vypíše ho počítač, většinou ještě s něčím před ním, +takže ho nepiš sám/sama! Zadej jen whoami a Enter.

+

Stejně tak počítač sám vypíše přihlašovací jméno.

+

Aktuální adresář +# +

+

Příkazová řádka pracuje vždy v nějakém adresáři neboli složce +(angl. directory, folder). +Adresář a složka jsou synonyma; můžeš používat kterékoli z nich.

+

Ve kterém adresáři zrovna jsi, to ti poví příkaz, který se podle systému +jmenuje pwd nebo cd (z angl. print working directory – vypiš pracovní +adresář, resp. current directory – aktuální adresář).

+
+

Unix

$ pwd
+/home/helena/
+
+

Windows

> cd
+C:\Users\helena
+
+ +

Aktuální adresář se většinou ukazuje i ve výzvě příkazové řádky, před znakem +$ nebo >. +Ale je dobré pwd/cd znát, kdyby ses náhodou ztratil/a. +Občas totiž bývá vypsaný zkráceně. +A taky třeba budeš v budoucnu muset pracovat na počítači který před $ +ukazuje něco jiného.

+

Něco jako aktuální adresář možná znáš z grafických programů, +kterými vybíráš soubory: typicky mají v horní (nebo na Macu dolní) +části uvedeno který adresář zrovna ukazují. +Příkazová řádka umí soubory ukazovat taky – ale musíš si o to říct.

+

Co v tom adresáři je? +# +

+

Příkaz ls nebo dir (z angl. list – vyjmenovat, resp. directory – adresář) +ti vypíše, co aktuální adresář obsahuje: všechny soubory, +včetně podadresářů, které se v aktuálním adresáři nacházejí.

+
+

Unix

$ ls
+Applications
+Desktop
+Downloads
+Music
+
+
+

Windows

> dir
+ Directory of C:\Users\helena
+05/08/2014 07:28 PM <DIR>  Applications
+05/08/2014 07:28 PM <DIR>  Desktop
+05/08/2014 07:28 PM <DIR>  Downloads
+05/08/2014 07:28 PM <DIR>  Music
+
+
+ +

Změna aktuálního adresáře +# +

+

Aktuální adresář se dá změnit pomocí příkazu cd +(z angl. change directory – změnit adresář). +Za cd se píše mezera a jméno adresáře, kam chceš přejít. +Pokud máš adresář Desktop nebo Plocha, přejdi tam. +Pak nezapomeň ověřit, že jsi na správném místě.

+

Používáš-li Linux nebo macOS, dej si pozor na velikost písmen: na těchto +systémech jsou Desktop a desktop dvě různá jména.

+

Používáš-li Windows, cd už jsi používal/a – tento příkaz se chová různě +podle toho, jestli něco napíšeš za něj nebo ne.

+
+

Unix

$ cd Desktop
+$ pwd
+/home/helena/Desktop
+
+

Windows

> cd Desktop
+> cd
+C:\Users\helena\Desktop
+
+ +

Poznámka pro Windows

+

Pokud přecházíš do adresáře na jiném disku, +například D: místo C:, je potřeba kromě cd +zadat jméno disku s dvojtečkou jako zvláštní příkaz (např. D:).

+

Vytvoření adresáře +# +

+

Co takhle si zkusit vytvořit adresář? To se dělá příkazem mkdir +(z angl. make directory – vytvořit adresář). +Za tento příkaz napiš jméno adresáře, který chceš vytvořit – v našem případě +zkouska:

+
+

Unix

$ mkdir zkouska
+
+

Windows

> mkdir zkouska
+
+ +

Vypiš si teď obsah aktuálního adresáře pomocí ls nebo dir. +Jeden z vypsaných adresářů bude zkouska.

+

Když je adresář vytvořený, můžeš do něj přejít podobně jako jsi před chvílí +přešel/přešla na Desktop nebo Plocha:

+
+

Unix

$ cd zkouska
+
+

Windows

> cd zkouska
+
+ +

V grafickém hledátku +# +

+

Často nebudeš pracovat jenom s příkazovou řádkou. +Vyplatí se umět aktuální adresář z příkazové řádky otevřít i v jiných +programech.

+

Otevři si prohlížeč souborů. +Tenhle program je na každém systému jiný:

+
+
+

Linux

+ Screenshot programu Nautilus na GNOME +
+
+

macOS

+ Screenshot programu Finder na macOS +
+
+

Windows

+ Screenshot průzkumníka na Windows +
+
+ +

Možná umíš v tomhle programu klikáním „donavigovat“ do adresáře, který je +aktivní v příkazové řádce. +V budoucnu to ale bude složitější, takže bude dobré si vyzkoušet kopírovat text +z příkazové řádky a vložit ho do prohlížeče souborů.

+

Bohužel se to dělá na každém systému jinak. +A protože známé zkratky Ctrl+C a +Ctrl+V dělají v příkazové řádce něco jiného než +kopírování, nejspíš se to dělá jinak, než jsi zvyklý/á.

+

Nejdřív si pomocí příkazu cd nebo pwd nech vypsat celé jméno adresáře +zkouska:

+
+

Unix

$ pwd
+/home/helena/Desktop/zkouska
+
+

Windows

> cd
+C:\Users\helena\Desktop\zkouska
+
+ +

Kopírování z příkazové řádky +# +

+

Na Linuxu vyber text myší a pak buď:

+
    +
  • pravým tlačítkem myši otevři menu a vyber Kopírovat nebo Copy, nebo
  • +
  • zmáčkni Ctrl+Shift+C. +(Pozor, v příkazové řádce musíš použít navíc Shift.)
  • +
+

Na macOS vyber text myší a pak stiskni ⌘ Command+C.

+

Na Windows v menu příkazové řádky (ikonce vlevo nahoře) vyber +EditMark, text vyber myší a zkopíruj pomocí Enter.

+

Otevření v prohlížeči souborů +# +

+

Na Linuxu záleží na programu, který používáš. Buď:

+
    +
  • zmáčkni Ctrl+L a jméno adresáře vlož pomocí +Ctrl+V, nebo
  • +
  • vyber jméno adresáře v horní části, smaž ho a vlož nové pomocí +Ctrl+V. +V obou případech potvrď pomocí Enter
  • +
+

Na macOS vyber v menu GoGo to Folder, vlož jméno adresáře pomocí +⌘ Command+V a potvrď pomocí Enter.

+

Na Windows klikni na jméno adresáře v horní části. +Převede se tím na editovatelný text. +Smaž ho a pomocí Ctrl+V místo něj vlož nové jméno. +Potvrď pomocí Enter.

+

Teď se můžeš podívat na Plochu nebo do nějakého grafickém programu na +prohlížení adresářů: zjistíš, že se adresář opravdu vytvořil.

+

Vkládání do příkazové řádky +# +

+

Občas si otevřeš soubor v prohlížeči souborů a budeš do něj chtít přejít +v příkazové řádce. +Zkopírování jména adresáře doufám nebude problém; vkládání do příkazové řádky +je ale občas jiné než v ostatních programech:

+
    +
  • Linux: Ctrl+Shift+V
  • +
  • macOS: ⌘ Command+V
  • +
  • Windows: Menu EditPaste
  • +
+

Pokud jsou ve jménu mezery nebo jiné speciální znaky jako *#$%^()><;"?, +musíš ho v příkazové řádce ještě uzavřít do uvozovek: před a za jméno napiš +", např:

+
$ cd "můj super adresář"
+

Lepší je ale mezery a zvláštní znaky ve jménech souborů nepoužívat.

+

Pozorování změn +# +

+

Vyzkoušej si, že se v řádce projeví i změny, které na počítači +uděláš jiným způsobem.

+

V grafickém prohlížeči, který se „dívá“ na stejný adresář, jako máš aktivní +v příkazové řádce, vytvoř nový soubor nebo adresář. +Pak se pomocí příkazu ls nebo dir podívej, že se opravdu vytvořil. +Potom ho v grafickém programu smaž – a v příkazové řádce se ujisti, +že je opravdu smazaný.

+

Na počítači máš jen jednu sadu souborů, se kterou umí manipulovat jak grafické +programy tak příkazová řádka.

+

O úroveň nahoru +# +

+

A poslední věc: jsi-li teď v adresáři Desktop/zkouska (nebo Plocha/zkouska, +Desktop\zkouska atp.), jak se dostat zpátky do Desktop?

+

Příkaz cd Desktop fungovat nebude: tím bys počítači řekl/a, ať se přepne +do adresáře Desktop v aktuálním adresáři. +Ale v adresáři zkouska žádný Desktop není! +Je to naopak: zkouska je v Desktop. +Odborně řečeno, adresář Desktop je nadřazený aktuálnímu adresáři.

+

Nadřazený adresář má speciální jméno .., dvě tečky. +Přejdi do něj zadáním cd .. a pak se ujisti, že jsi opravdu v Desktop:

+
+

Unix

$ cd ..
+$ pwd
+/home/helena/Desktop
+
+

Windows

> cd ..
+> cd
+C:\Users\helena\Desktop
+
+ +

Další cd .. by tě přesunulo do dalšího nadřazeného adresáře – v našem +příkladu helena.

+

Konec +# +

+

Příkazů existuje samozřejmě daleko víc.

+

Když se je naučíš, můžeš z příkazové řádky plnohodnotně ovládat +počítač: vytvářet soubory, mazat je, pouštět programy, měnit nastavení +a podobně. +Vydalo by to ale vydalo na samostatný kurz, a tak tady skončíme.

+

Vyzkoušej si ještě jeden příkaz, ten, který příkazovou řádku zavírá: exit.

+

Příkaz exit funguje stejně na všech systémech. +To samé platí pro překvapivě mnoho příkazů (kromě těch základních jako +cd, pwd a ls). +Proto ve zbytku těchto materiálů nebudu používat ukázky dvojmo podle +operačního systému.

+

A budu používat unixovskou výzvu $. +S touto konvencí se setkáš i ve většině návodů na internetu. +Používáš-li Windows, je dobré si na $ zvyknout i když ve své +řádce máš místo něj >.

+

Zkus si tedy, co dělá příkaz exit:

+
$ exit
+

A tím je úvod do příkazové řádky hotový.

+

Přehled +# +

+

Tady je tabulka základních příkazů, se kterými si do začátku vystačíš:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
UnixWindowsPopisPříklad
cd adresářcd adresářzměna adresářecd test
cd ..
pwdcdvýpis aktuálního adresářepwd
cd
lsdirvýpis adresářels
dir
exitexitukončeníexit
+ +

Další příkazy jako python nebo git si vysvětlíme až budou potřeba, +po tom, co si je nainstaluješ.

\ No newline at end of file diff --git a/lessons/beginners/cmdline/static/linux-file-browser.png b/lessons/cmdline/linux-file-browser.png similarity index 100% rename from lessons/beginners/cmdline/static/linux-file-browser.png rename to lessons/cmdline/linux-file-browser.png diff --git a/lessons/beginners/cmdline/static/macos-file-browser.png b/lessons/cmdline/macos-file-browser.png similarity index 100% rename from lessons/beginners/cmdline/static/macos-file-browser.png rename to lessons/cmdline/macos-file-browser.png diff --git a/lessons/beginners/cmdline/static/windows-cmd-properties.png b/lessons/cmdline/windows-cmd-properties.png similarity index 100% rename from lessons/beginners/cmdline/static/windows-cmd-properties.png rename to lessons/cmdline/windows-cmd-properties.png diff --git a/lessons/beginners/cmdline/static/windows-file-browser.png b/lessons/cmdline/windows-file-browser.png similarity index 100% rename from lessons/beginners/cmdline/static/windows-file-browser.png rename to lessons/cmdline/windows-file-browser.png diff --git a/lessons/git/collaboration/static/gh-workflow-diagram.svg b/lessons/collaboration/gh-workflow-diagram.svg similarity index 100% rename from lessons/git/collaboration/static/gh-workflow-diagram.svg rename to lessons/collaboration/gh-workflow-diagram.svg diff --git a/lessons/collaboration/index.html b/lessons/collaboration/index.html new file mode 100644 index 00000000..3198a583 --- /dev/null +++ b/lessons/collaboration/index.html @@ -0,0 +1,389 @@ +

Spolupráce +# +

+

„Opravdové” programy zřídka vznikají prací jednoho člověka. +Víc hlav víc ví, a tak je dobré si na projekt vytvořit tým.

+

Každý člen týmu potřebuje mít přístup k práci ostatních. +K tomu se dá použít Git: někde na internetu si zařídí sdílený repozitář, +se kterým se všichni budou synchronizovat.

+

Pro samostudium

+

Pokud materiály čteš z domu a máš možnost se +v budoucnu dostat na nějaký sraz, zatím tuhle sekci přeskoč. +Na sraze pak popros zkušenější programátory, aby ti pomohli. +(Nechystáš-li se na sraz, můžeš pokračovat – +zvládnout se to dá.)

+

Pro kouče

+

Udělej na GitHubu repozitář jménem prezencka a dej do +něj soubor se svým jménem. Příklad je na +naucse/prezencka. +Nasdílej s účastníky příkaz na jeho naklonování (přes https).

+

Open Source +# +

+

Nejde mluvit o Gitu a spolupráci a nezastavit se chvíli u otevřeného +zdrojového kódu. +První programy vznikaly v akademické sféře, kde byly zcela přirozeně sdíleny, +jako je to s poznatky mezi vědci běžné. +50. a 60. léta byla obdobím velké kreativity, kdy vzniklo mnoho z konceptů +a technologií, které dnes používáme. +Pak se začalo programování postupně komercializovat a firmy začaly zdrojový +kód skrývat jako konkurenční výhodu. +Do té doby víceméně jednolitá komunita programátorů byla nucena se rozdělit.

+

Některým programátorům tohle skrývání kódu hluboce vadilo. +Roku 1985 publikoval +Richard Stallman +GNU Manifesto, +kde vysvětlil, proč hodlá vytvořit operační systém s otevřeným kódem a +odstartoval tak hnutí svobodného softwaru. +To prosazuje 4 následující svobody (převzato +z Wikipedie):

+
    +
  1. svoboda používat program za jakýmkoliv účelem,
  2. +
  3. svoboda studovat, jak program pracuje a možnost přizpůsobit ho svým potřebám,
  4. +
  5. svoboda redistribuovat kopie programu,
  6. +
  7. svoboda vylepšovat program a zveřejňovat zlepšení, aby z nich mohla mít prospěch celá komunita.
  8. +
+ +

Dnes je spousta projektů s otevřeným zdrojovým kódem (tzv. open-source projektů) +dostupná na Internetu a každý je používáme. +Jejich další sdílení je upraveno jednou z licencí, +které tyto základní svobody zaručují.

+

Ne všechny jsou v Pythonu (a těm co jsou zatím +nebudeš všem rozumět). Ne všechny jsou v Gitu. +Ne všechny jsou kvalitní – protože si +každý může zveřejnit co chce, na Internetu se válí +spousta nedodělků, opuštěných nápadů a nepodařených experimentů. +A bohužel, ne všechny projekty mají přátelské autory.

+

Na druhou stranu ale open-source programy +mají svoje výhody: nejenom že se z nich může kdokoli +učit, ale každý může i zkontrolovat, jestli +dělají to, co dělat mají. +Populární open-source programy tě například +pravděpodobně nebudou špehovat (tj. hlásit autorovi, +co na počítači děláš), ani většinou neobsahují +reklamy: kdyby to dělaly, najde se +někdo kdo tyhle „funkce” odstraní a lidi – časem – +začnou používat opravenou verzi.

+

Některé příklady populárních open-source projektů:

+ +

Jak vidno z posledního příkladu, nejen softwarové +projekty se dají vést takhle veřejně. +Tento kurz vychází z principů open source: +všechno know-how je sdílené a budeme rádi, když +se zapojíš.

+

Až příště uvidíš v materiálech chybu (nebo jestli o nějaké víž už teď), +dnes se dozvíš, jak ji opravit!

+

A co tvůj kód? Chceš ho taky dávat takhle veřejně k dispozici? +Nutné to samozřejmě není – Git se dá používat i +v uzavřeném týmu – ale na druhou stranu, +máš důvod proč to nedělat? +Zveřejňovat zdrojový kód se hodí už jen pro to, +aby ti s ním mohli zkušenější programátoři snadněji pomáhat.

+

GitHub +# +

+

Na Internetu existuje spousta stránek, kam se dají nahrávat gitové repozitáře +s kódem – např. GitLab, +BitBucket, +Pagure nebo +Launchpad. +Aktuálně nejpopulárnější je ale GitHub, který si tady +ukážeme.

+

Jestli ještě nemáš uživatelský účet na github.com, jdi +tam a založ si ho.

+

Naklonování repozitáře (git clone) +# +

+

Pro začátek zkusíme práci s repozitářem, který už vytvořil někdo jiný. +V příkazové řádce zadej příkaz, který ti oznámí kouč; něco jako

+
$ git clone https://github.com/naucse/prezencka
+

Vytvoří se ti nový repozitář – adresář se jménem +prezencka, ve kterém je nějaký soubor.

+

Na URL (adresu), kterou jsi v tomhle příkladě +použil/a, se můžeš podívat i v prohlížeči. +Uvidíš seznam souborů a spoustu odkazů k +informacím o repozitáři (například pod „commits” +je historie).

+

Přepni se do nového adresáře (cd prezencka) +a zkus se podívat na historii (gitk nebo git log). +Možná je krátká, ale hlavně, že nějaká je. +Máš na počítači kopii projektu, který založil někdo jiný!

+

Jak už napovídá název repozitáře, tvůj příspěvek do tohoto projektu bude +zápis do prezenčky: konkrétně přidání souboru s tvým jménem. +Jméno je to proto, aby nedocházelo ke kolizím: potřebujeme, aby příspěvky od +všech lidí, kteří prochází tenhle kurz, byly jiné.

+

Tvůj příspěvek bude ovšem veřejně vystaven na internetu. +Pokud nechceš vystavovat svoje občanské jméno, použij místo něj klidně +přezdívku, oblíbené jídlo nebo pár náhodných písmen. Ale:

+
    +
  • když budeš pojmenovávat soubor, buď originální, aby nedošlo ke konfliktům, a
  • +
  • nesdílej nic, co nemáš právo sdílet (např. texty moderních písní).
  • +
+

Vytvoření větve +# +

+

Pomocí git branch zjisti, na jaké jsi aktuálně větvi. +Měla by to být větev master.

+

Tuhle „základní“ větev je dobré používat jen na revize, na kterých se už +shodl celý tým. +Proto když chceš do projektu přispět, jako první krok si pro svůj příspěvek +udělej novou větev a přepni se do ní. +Například pomocí:

+
$ git branch pridani-jmena
+$ git checkout pridani-jmena
+

Posílání změn (git push) +# +

+

Teď se do projektu zapoj. +Přidej soubor pojmenovaný podle tvého jména (nebo přezdívky) +a dej ho do gitu (git add ...; git commit).

+

Teď zbývá „jen” změnu začlenit do původního sdíleného repozitáře. +To ale není jen tak: repozitář, který jsi +naklonoval/a, patří koučovi. A tomu by se asi +nelíbilo, kdyby kdokoliv na Internetu mohl přijít +a nahrát mu do repozitáře změny.

+

Spousta míst na Internetu (blogy, zpravodajství, e-shopy) funguje tak, že +vybraná skupina lidí, „editorů“, má právo měnit obsah, jak se jim líbí. +Takovým editorům musí správce projektu věřit, než jim přístup povolí.

+

S Gitem se používá trošku jiný mechanismus: +změny nahraješ do vlastního sdíleného +repozitáře, který máš právo měnit jen ty. +Majiteli původního projektu pak napíšeš +žádost o začlenění těch změn (angl. pull request). +Může to být třeba mail se slovy „Hele, na té a té +adrese mám nějaké změny, které by se ti mohly hodit! +Přidej je do svého projektu!”

+

Výhoda je v tom, že se do projektu – pokud je +veřejný – může zapojit kdokoliv. Nemusíš se +předem ptát, nemusíš dokazovat že jsi důvěryhodná +osoba, stačí něco změnit a poslat. +Jestli se změna bude autorům projektu líbit nebo +ne, to už je jiná věc – ale můžou posuzovat samotnou +změnu, ne důvěryhodnost jejího autora.

+

Služby jako github.com +ti umožňují si udělat vlastní sdílený repozitář (který bude k dispozici na +internetu) a zjednodušují začleňování změn (místo posílání mailů stačí +zmáčknout tlačítko). Pojďme se podívat, jak na to.

+

Přihlaš se na GitHub a pak zajdi na adresu +kterou jsi použil/a pro git clone. +Vlevo nahoře najdi tlačítko „Fork” a klikni na něj. +Tím si vytvoříš na GitHubu vlastní kopii repozitáře: +adresa by měla být něco jako +https://github.com/tvojejmeno/prezencka.

+

Kdybys měl/a v různých kopiích repozitáře zmatek, +přijde vhod malé vysvětlení: jedna kopie je původní +projekt na GitHubu, kam správce projektu dává +aktuální „oficiální“ nebo „hlavní“ verzi. Další kopie na GitHubu +je „tvoje“ a můžeš si do ní nahrát co chceš +(nejčastěji v ní ale zveřejňuješ změny, které můžou +být užitečné pro ostatní). +Tyhle dvě kopie existují na serverech GitHubu a jsou volně dostupné +přes internet.

+

Třetí kopii repozitáře pak máš u sebe na počítači. +K té se dostaneš jen ty.

+

Z „hlavní“ verze si stáhneš práci ostatních členů týmu; +do tvého projektu na GitHubu dáváš své změny, aby je ostatní mohli +schválit a začlenit do „hlavní“ verze.

+

Diagram tří repozitářů

+

A teď, jak z tvého počítače nahrát změny na GitHub? +Git si u každého repozitáře na tvém počítači +pamatuje adresy, odkud se dají stahovat +a kam se dají posílat změny. +Seznam těchhle adres ti ukáže příkaz git remote -v. +Třeba:

+
$ git remote -v
+origin  https://github.com/naucse/prezencka (fetch)
+origin  https://github.com/naucse/prezencka (push)
+

Tenhle výstup znamená, že pod zkratkou „origin” +se schovává adresa, ze které jsi repozitář +naklonoval/a.

+

Přidej si podobnou zkratku pro vlastní repozitář na GitHubu. +Nezapomeň nahradit tvojejmeno za jméno účtu, +který máš na GitHubu ty. (Pozor, v příkazu je tvojejmeno dvakrát!)

+
+
$ git remote add tvojejmeno https://github.com/tvojejmeno/prezencka
+
+ +

a zkontroluj si, že se to povedlo:

+
+
$ git remote -v
+origin  git@github.com:naucse/prezencka.git (fetch)
+origin  git@github.com:naucse/prezencka.git (push)
+tvojejmeno      https://github.com/tvojejmeno/prezencka (fetch)
+tvojejmeno      https://github.com/tvojejmeno/prezencka (push)
+
+ +

Tolik k nastavení – git remote add +stačí udělat jednou pro každý repozitář. +Pak už můžeš změny nahrávat pomocí:

+
+
$ git push tvojejmeno pridani-jmena
+
+ +

což znamená: pošli na adresu uloženou pod zkratkou +tvojejmeno +větev pridani-jmena.

+

Funguje? Podívej se na +https://github.com/tvojejmeno/prezencka +v prohlížeči a ujisti se, že tam tvoje změny jsou.

+

Žádost o začlenění (pull request) +# +

+

Teď zbývá požádat autory původního projektu, +aby změny z tvého sdíleného repozitáře přidali do svojí kopie. +GitHub na to má mechanismus zvaný pull request (žádost o začlenění).

+

Jdi na stránku původního projektu (na adresu, +kterou jsi použil/a na začátku pro +git clone). +Měl/a bys tam vidět oznámení o své nově nahrané větvi +s velkým zeleným tlačítkem Compare & pull request. +Klikni na něj. Pokud chceš, tak dopiš/změň popisek +toho, co tahle změna obnáší. +Pak zmáčkni další tlačítko.

+

Jestli tlačítko Compare & pull request nevidíš, běž na adresu +své kopie repozitáře a stiskni tlačítko New pull request. +Vyber, co kam chceš začlenit, dopiš/změň popisek a pak zmáčkni +Create pull request.

+

Hotovo; teď je na autorech projektu, aby +se na změny podívali a přijali – nebo začali diskusi +o tom, jak je ještě vylepšit. +(Diskutovat se dá na stránce pull requestu nebo přes mail.)

+

Pro samostudium

+

Procházíš-li materiály z domu, musíš teď počkat, +než si někdo tvé žádosti všimne a začlení ji. +To může trvat i pár dní; kdyby to bylo přes týden, +tak se na stránce pull requestu zkus připomenout.

+

U přidání jména do prezenčky se to asi nestane, ale kdybys potřeboval/a +na změně před začleněním ještě trochu zapracovat (třeba i po +pár dnech diskuse), nebyl by to problém. +Přepni se na svém počítači do větve pridani-jmena, udělej další revize, +a pomocí git push tvojejmeno pridani-jmena +pull request aktualizuj.

+

Aktualizace (git pull) +# +

+

Když budou tvé změny – a změny od ostatních – +začleněné, můžeš si aktualizovat lokální repozitář. (To je ten, +který máš u sebe na počítači.)

+

Nejdřív se přepni zpět do větve master. +Teď už nebudeš pracovat na pridani-jmena; tahle větev už je odeslaná.

+

To se dělá příkazem +git pull origin master (stáhni změny +z větve „master” z adresy pod zkratkou „origin”). +Pomocí gitk --all nebo git log +se můžeš podívat, jak se projekt mezitím vyvinul.

+

Tohle git pull je dobré provést vždycky předtím, než začneš pracovat na +nové změně/větvi. +Zaručíš tím, že projekt, který měníš, je „čerstvý“.

+

Gratuluji! Právě jsi prošel/prošla „kolečkem“, +které většina programátorů dělá denně: udělání nějaké změny, +odeslání kolegům na kontrolu a začlenění a stažení změn od ostatních.

+

Hlášení chyb (issues) +# +

+

Občas nastane situace, kdy v nějakém projektu +na GitHubu najdeš chybu, ale nemáš čas nebo +znalosti, abys ji opravil/a. V takovém případě +často na GitHubu na stránce projektu pod záložkou Issues +najdeš seznam nahlášených problémů. +Nenajdeš-li mezi nimi „svoji” chybu, můžeš ji +nahlásit – stačí kliknout na New Issue +a můžeš psát, kdy chyba nastává, co program dělá +špatně a co by měl dělat místo toho.

+

Některé projekty nepoužívají Issues na GitHubu. +Kdybys záložku Issues nenašel/nenašla, podívej se +do dokumentace projektu, jestli tam není odkaz na +seznam chyb.

+

README: Informace pro ostatní +# +

+

Pokud vytváříš projekt a chceš, aby do něj přispívali i ostatní, +je potřeba aby věděli, co tvůj projekt dělá, k čemu se hodí, +jak se používá a podobně.

+

Na základní informace o projektu/repozitáři se používá soubor README +(z angl. read me, čti mě). +Do tohoto souboru patří mj.:

+
    +
  • název projektu,
  • +
  • stručný popis projektu (jedna až dvě věty),
  • +
  • krátký návod k instalaci projektu,
  • +
  • krátký návod ke spuštění projektu,
  • +
  • krátký návod k používání projektu, případně odkaz na rozsáhlejší dokumentaci,
  • +
  • pokud má projekt testy, informace o tom, jak je spustit,
  • +
  • informace o tom, jak se zapojit do vývoje projektu,
  • +
  • informace o autorech projektu,
  • +
  • informace o licenci (více se licencích dozvíš později).
  • +
+

README by mělo být členěné a jeho přečtení by nemělo zabrat uživateli hodinu, +většinou stačí krátké úderné informace s případným odkazem někam dál. +Nemusíš tedy například vysvětlovat v každém projektu, jak se instaluje Python. +Stačí říct, že Python je potřeba (a v jaké verzi) +a odkázat uživatele na patřičný návod. +Je také třeba brát v úvahu, kdo bude README číst. +Píšeš-li program pro jiné vývojářky a vývojáře, +často nemusíš zabrušovat do detailů.

+

GitHub (a spousty jiných podobných služeb) umožňuje pro README použít nějaký +značkovací jazyk, například Markdown. +Je možné pak používat nadpisy, obrázky apod.

+

A v neposlední řadě: aby se do projektu mohl zapojit +kdokoli z celého světa, bývají open-source projekty v angličtině. +Jména proměnných, komentáře, dokumentace – všechno +je primárně v anglické verzi. +Tenhle kurz je česky, aby byly začátky jednodušší, +ale jestli se ti programování zalíbilo a chceš +v něm po kurzu pokračovat dál, bez angličtiny +to bude velice složité.

+

Licence +# +

+

Aby sdílení fungovalo i pro právní stránce, +nestačí když nahraješ kus kódu na Internet. +Musíš taky oficiálně oznámit, že si s ním ostatní můžou hrát. +Na svůj kód totiž máš autorské právo, podle kterého ostatní nesmí tvůj program +používat, natož vylepšovat, dokud jim to nepovolíš. +Pro formální udělení tohohle povolení se používají licence, které píšou +právníci.

+

Problematika licencí může být, bohužel, docela složitá. +Když to ale zjednodušíme na minimum, budeš +chtít jen zajistit, aby každý mohl tvůj výtvor +používat, učit se z něj, předávat ho dál +a vylepšovat ho. V tom případě vyber třeba +licenci MIT.

+

Pokud chceš navíc zabránit tomu, že si tvůj kód +někdo vezme a začne ho „vylepšovat“ a vydělávat na +něm, aniž by se o vylepšení podělil s ostatními, +zkus licenci AGPL.

+

A tyto materiály jsou pod ještě jinou licencí – +CC BY-SA – +protože výše jmenované licence jsou dělané na programy, ne na text.

+

Kód se nejčastěji licencuje tak, že text licence +dáš do souboru jménem LICENSE a přidáš do Gitu. +Je dobré licenci zmínit i v souboru README.

+

Chceš-li si o licencích přečíst něco víc, odkážu tě na +choosealicense.com, +případně creativecommons.org +a opensource.org.

\ No newline at end of file diff --git a/lessons/comparisons/index.html b/lessons/comparisons/index.html new file mode 100644 index 00000000..a638ed2f --- /dev/null +++ b/lessons/comparisons/index.html @@ -0,0 +1,198 @@ +

Porovnávání +# +

+

Pamatuješ si ještě, co je to operátor?

+

V domácím projektu jsme si ukázali základní aritmetické operátory. +Přidáme-li jeden další (//), jsou to tyhle:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
SymbolPříkladPopis
+, -, *, /1 + 1Základní aritmetika
--5Negace
//; %7 // 2; 7 % 2Dělení se zbytkem (celočíselné dělení); zbytek po dělení
**3 ** 2Umocnění (3 na druhou)
+ +

Python ale zná i další druhy operátorů. +Důležité jsou operátory porovnávací. +Zkus si co dělají! +(Buď z programu pomocí print, +nebo pusť python z příkazové řádky.)

+ + + + + + + + + + + + + + + + + + + + + +
SymbolPříkladPopis
==, !=1 == 1, 1 != 1Je rovno, není rovno
<, >3 < 5, 3 > 5Menší než, větší než
<=, >=3 <= 5, 3 >= 5Menší nebo rovno, větší nebo rovno
+ +

Hodnoty porovnání jsou takzvané booleovské hodnoty +(angl. boolean, podle G. Boolea). +V Pythonu je můžeš použít vždycky, když potřebuješ vědět, jestli něco platí +nebo neplatí. +Jsou jenom dvě – buď True (pravda), nebo False (nepravda).

+

Jako všechny hodnoty, True a False můžeš přiřadit do proměnných:

+
pravda = 1 < 3
+print(pravda)
+
+nepravda = 1 == 3
+print(nepravda)
+

Všimni si, že rovnost zjistíš pomocí dvou rovnítek: 3 == 3. +Jedno rovnítko přiřazuje do proměnné; dvě rovnítka porovnávají.

+

Slova True a False můžeš +v programu použít i přímo, +jen si dej pozor na velikost písmen:

+
print(True)
+print(False)
+

Podmínky +# +

+

Teď oprášíme program na výpočet obvodu a obsahu.

+

Otevři si v editoru nový soubor. +Jestli ještě v adresáři, kde máš soubory ke kurzům Pythonu, +nemáš adresář pro tuto lekci (třeba 02), vytvoř si ho. +Nový soubor ulož do něj pod jménem if.py.

+

Do souboru pak napiš následující program:

+
strana = float(input('Zadej stranu čtverce v centimetrech: '))
+print('Obvod čtverce se stranou', strana, 'je', 4 * strana, 'cm')
+print('Obsah čtverce se stranou', strana, 'je', strana * strana, 'cm2')
+

Program spusť. Funguje?

+

Co se stane, když jako stranu zadáš záporné číslo? +Dává výstup smysl?

+

Tady je vidět, jak počítač dělá přesně, co se mu řekne. Nepřemýšlí o významu. +Bylo by dobré uživateli, který zadá záporné číslo, +přímo říct, že zadal blbost. Jak na to?

+

Nejdřív zkus nastavit proměnnou, která bude True, +když uživatel zadal kladné číslo.

+
+

Řešení

+ + +
+ +

A nyní řekni počítači, aby se na základě hodnoty této proměnné rozhodl, co má udělat. +K tomu můžeš použít dvojici příkazů if (pokud) +a else (jinak):

+
strana = float(input('Zadej stranu čtverce v centimetrech: '))
+cislo_je_spravne = strana > 0
+
+if cislo_je_spravne:
+    print('Obvod čtverce se stranou', strana, 'je', 4 * strana, 'cm')
+    print('Obsah čtverce se stranou', strana, 'je', strana * strana, 'cm2')
+else:
+    print('Strana musí být kladná, jinak z toho nebude čtverec!')
+
+print('Děkujeme za použití geometrické kalkulačky.')
+

Neboli: po if následuje podmínka (angl. condition), +což je výraz, podle kterého se budeme rozhodovat. +Za podmínkou je dvojtečka. +Potom následují příkazy, které se provedou, pokud je podmínka pravdivá. +Všechny jsou odsazeny o čtyři mezery.

+

Čtyři mezery neznamenají, že musíš čtyřikrát zmáčknout mezerník! +K odsazení použij klávesu Tab, která vloží správný počet mezer. +(Pokud ne, nemáš správně nastavený editor – podívej se do lekce o instalaci.) +Pomocí Shift+Tab můžeš odsazení zase zmenšit.

+

A ani Tab není vždycky potřeba. +Pokud napíšeš řádek s if bez chyby, některé editory za tebe další řádek odsadí automaticky.

+

Po téhle části stačí napsat neodsazené else:, zase s dvojtečkou na konci, +a odsazené příkazy, které se provedou v opačném případě.
+Potom můžeš psát příkazy, které se provedou vždycky – ty odsazené nebudou, +podmíněná část programu už skončila.

+

Vzato čistě technicky, odsazení nemusí být o čtyři mezery. +Může být třeba o dvě nebo o jedenáct, nebo se dokonce dá místo mezer použít +tabulátor. +V rámci jednoho bloku musí být ale odsazení vždycky stejné, +takže když pak na jednom programu spolupracuje více lidí, musí se shodnout. +No a na čtyřech mezerách se shodla většina Pythonního světa.

+

Další podmíněné příkazy +# +

+

Někdy není else vůbec potřeba. +V následujícím programu se nedělá nic navíc, pokud je číslo nenulové:

+
cislo = int(input('Zadej číslo, přičtu k němu 3: '))
+if cislo == 0:
+    print('Jé, to je jednoduché!')
+print(cislo, '+ 3 =', cislo + 3)
+

Někdy je naopak potřeba podmínek několik, +k čemuž slouží příkaz elif – kombinace else a if. +Dává se „mezi“ bloky if a else. +Příkazů elif může být za jedním if-em několik, +ale vždy se provede jen jedna „větev“: +ta první, jejíž podmínka je splněna.

+
vek = int(input('Kolik ti je let? '))
+if vek >= 150:
+    print('A ze kterépak jsi planety?')
+elif vek >= 18:
+    # Tahle větev se např. pro "200" už neprovede.
+    print('Můžeme nabídnout: víno, cider, nebo vodku.')
+elif vek >= 1:
+    print('Můžeme nabídnout: mléko, čaj, nebo vodu')
+elif vek >= 0:
+    print('Sunar už bohužel došel.')
+else:
+    # Nenastala ani nedna ze situací výše – muselo to být záporné
+    print('Pro návštěvy z budoucnosti bohužel nemáme nic v nabídce.')
+

Zanořování +# +

+

Příkazy if se dají zanořovat (angl. nest). +V odsazeném (podmíněném) bloku kódu může být další if s dalším odsazeným +kódem. +Třeba u tohoto programu, který rozdává nejapné rady do života:

+
stastna = input('Jsi šťastná?')
+bohata = input('Jsi bohatá?')
+
+if stastna == 'ano':
+    # Tenhle kus kódu se provede, když je "šťastná"
+    if bohata == 'ano':
+        print('Gratuluji!')
+    else:
+        print('Zkus míň utrácet.')
+else:
+    # Tenhle kus kódu se provede, když není "šťastná"
+    if bohata == 'ano':
+        print('Zkus se víc usmívat!')
+    else:
+        print('To je mi líto.')
+
\ No newline at end of file diff --git a/lessons/course.json b/lessons/course.json new file mode 100644 index 00000000..9f1839df --- /dev/null +++ b/lessons/course.json @@ -0,0 +1,5376 @@ +{ + "api_version": [ + 0, + 4 + ], + "course": { + "description": "Seznam udržovaných lekcí bez ladu a skladu.", + "edit_info": { + "branch": "main", + "url": "https://github.com/pyvec/naucse-python" + }, + "lessons": { + "advanced/generators": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017." + ], + "content": { + "path": "generators/index.html" + }, + "ids": [ + "dalsi_pouziti_generatoru", + "delegace_na_podgenerator_codeyield_fromcode", + "generatory", + "iterace", + "obousmerna_komunikace", + "vraceni_hodnot_z_generatoru" + ], + "license": "cc-by-sa-40", + "links": [ + "#dalsi_pouziti_generatoru", + "#delegace_na_podgenerator_codeyield_fromcode", + "#generatory", + "#iterace", + "#obousmerna_komunikace", + "#vraceni_hodnot_z_generatoru" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/advanced/generators/index.md", + "title": "Generátory", + "vars": {} + } + }, + "source_file": "lessons/advanced/generators/info.yml", + "static_files": {}, + "title": "Generátory" + }, + "beginners/and-or": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "and-or/index.html" + }, + "ids": [ + "emneboem_anebo_emaem", + "stastnabohata" + ], + "license": "cc-by-sa-40", + "links": [ + "#emneboem_anebo_emaem", + "#stastnabohata", + "naucse:page?lesson=beginners/comparisons", + "naucse:page?lesson=beginners/exceptions" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/and-or/index.md", + "title": "Nebo anebo a", + "vars": {} + } + }, + "source_file": "lessons/beginners/and-or/info.yml", + "static_files": {}, + "title": "Nebo anebo a" + }, + "beginners/basic-functions": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2019." + ], + "content": { + "path": "basic-functions/index.html" + }, + "ids": [ + "a_dalsi", + "matematicke_funkce", + "nahoda", + "prevadeni_a_codeinputcode", + "prevadeni_typu", + "solution-0", + "uzitecne_funkce", + "vstup_a_vystup" + ], + "license": "cc-by-sa-40", + "links": [ + "#a_dalsi", + "#matematicke_funkce", + "#nahoda", + "#prevadeni_a_codeinputcode", + "#prevadeni_typu", + "#uzitecne_funkce", + "#vstup_a_vystup", + "https://cs.wikipedia.org/wiki/Radi%C3%A1n", + "https://docs.python.org/3/library/functions.html", + "https://docs.python.org/3/library/math.html", + "https://github.com/pyvec/cheatsheets/raw/master/basic-functions/basic-functions-cs.pdf", + "naucse:solution?solution=0" + ], + "slug": "index", + "solutions": [ + { + "content": "

Funkce input vrací hodnotu, se kterou může program dál pracovat.\nZařadil bych ji tedy mezi „normální“ funkce.

\n

Jako argument bere input otázku, na kterou se uživatele zeptá.

\n

Návratová hodnota funkce input je řetězec s odpovědí uživatele.

" + } + ], + "source_file": "lessons/beginners/basic-functions/index.md", + "title": "Užitečné funkce", + "vars": {} + } + }, + "source_file": "lessons/beginners/basic-functions/info.yml", + "static_files": {}, + "title": "Užitečné funkce" + }, + "beginners/circular-imports": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "circular-imports/index.html" + }, + "ids": [ + "cyklicke_importy", + "organizace_modulu_podle_zavislosti", + "pomocny_modul" + ], + "license": "cc-by-sa-40", + "links": [ + "#cyklicke_importy", + "#organizace_modulu_podle_zavislosti", + "#pomocny_modul" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/circular-imports/index.md", + "title": "Cyklické importy", + "vars": {} + } + }, + "source_file": "lessons/beginners/circular-imports/info.yml", + "static_files": {}, + "title": "Cyklické importy" + }, + "beginners/class": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2015-2017." + ], + "content": { + "path": "class/index.html" + }, + "ids": [ + "atributy", + "hodnoty_a_objekty", + "metoda_code__init__code", + "metoda_code__str__code", + "parametr_codeselfcode", + "tridy", + "tvoreni_objektu_tridy", + "vlastni_tridy" + ], + "license": "cc-by-sa-40", + "links": [ + "#atributy", + "#hodnoty_a_objekty", + "#metoda_code__init__code", + "#metoda_code__str__code", + "#parametr_codeselfcode", + "#tridy", + "#tvoreni_objektu_tridy", + "#vlastni_tridy", + "naucse:page?lesson=beginners/functions", + "naucse:page?lesson=beginners/inheritance" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/class/index.md", + "title": "Třídy", + "vars": {} + } + }, + "source_file": "lessons/beginners/class/info.yml", + "static_files": {}, + "title": "Třídy" + }, + "beginners/cmdline": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017.", + "Založeno na tutoriálu [Django Girls].\n[Django Girls]: https://tutorial.djangogirls.org/en/intro_to_command_line/" + ], + "content": { + "path": "cmdline/index.html" + }, + "ids": [ + "aktualni_adresar", + "co_v_tom_adresari_je", + "konec", + "kopirovani_z_prikazove_radky", + "o_uroven_nahoru", + "otevreni_vprohlizeci_souboru", + "pozorovani_zmen", + "prehled", + "prikazova_radka", + "prvni_prikaz", + "vgrafickem_hledatku", + "vkladani_do_prikazove_radky", + "vytvoreni_adresare", + "zmena_aktualniho_adresare" + ], + "license": "cc-by-sa-40", + "links": [ + "#aktualni_adresar", + "#co_v_tom_adresari_je", + "#konec", + "#kopirovani_z_prikazove_radky", + "#o_uroven_nahoru", + "#otevreni_vprohlizeci_souboru", + "#pozorovani_zmen", + "#prehled", + "#prikazova_radka", + "#prvni_prikaz", + "#vgrafickem_hledatku", + "#vkladani_do_prikazove_radky", + "#vytvoreni_adresare", + "#zmena_aktualniho_adresare", + "naucse:static?filename=linux-file-browser.png", + "naucse:static?filename=macos-file-browser.png", + "naucse:static?filename=windows-cmd-properties.png", + "naucse:static?filename=windows-file-browser.png" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/cmdline/index.md", + "title": "Příkazová řádka", + "vars": {} + } + }, + "source_file": "lessons/beginners/cmdline/info.yml", + "static_files": { + "linux-file-browser.png": { + "path": "cmdline/linux-file-browser.png" + }, + "macos-file-browser.png": { + "path": "cmdline/macos-file-browser.png" + }, + "windows-cmd-properties.png": { + "path": "cmdline/windows-cmd-properties.png" + }, + "windows-file-browser.png": { + "path": "cmdline/windows-file-browser.png" + } + }, + "title": "Příkazová řádka" + }, + "beginners/comparisons": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "comparisons/index.html" + }, + "ids": [ + "dalsi_podminene_prikazy", + "podminky", + "porovnavani", + "solution-0", + "zanorovani" + ], + "license": "cc-by-sa-40", + "links": [ + "#dalsi_podminene_prikazy", + "#podminky", + "#porovnavani", + "#zanorovani", + "http://en.wikipedia.org/wiki/George_Boole", + "naucse:solution?solution=0" + ], + "slug": "index", + "solutions": [ + { + "content": "

Taková proměnná se dá nastavit pomocí tohoto kódu:

\n
strana = float(input('Zadej stranu čtverce v centimetrech: '))\ncislo_je_spravne = strana > 0\n
" + } + ], + "source_file": "lessons/beginners/comparisons/index.md", + "title": "Porovnávání", + "vars": {} + } + }, + "source_file": "lessons/beginners/comparisons/info.yml", + "static_files": {}, + "title": "Porovnávání" + }, + "beginners/def": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "def/index.html" + }, + "ids": [ + "cviceni", + "definice_funkce", + "definice_funkci", + "kcemu_jsou_funkce", + "solution-0", + "vraceni_ukoncuje_funkci" + ], + "license": "cc-by-sa-40", + "links": [ + "#cviceni", + "#definice_funkce", + "#definice_funkci", + "#kcemu_jsou_funkce", + "#vraceni_ukoncuje_funkci", + "naucse:page?lesson=beginners/functions", + "naucse:solution?solution=0" + ], + "slug": "index", + "solutions": [ + { + "content": "
def obsah_obdelnika(a, b):\n    return a * b\n\nprint('Obsah obdélníka se stranami 3 cm a 5 cm je', obsah_obdelnika(3, 5), 'cm2')\n
" + } + ], + "source_file": "lessons/beginners/def/index.md", + "title": "Definice funkcí", + "vars": {} + } + }, + "source_file": "lessons/beginners/def/info.yml", + "static_files": {}, + "title": "Definice funkcí" + }, + "beginners/dict": { + "pages": { + "index": { + "attribution": [ + "Část této kapitoly je založena na materiálech DjangoGirls.", + "Původní DjangoGirls tutoriál přeložila do češtiny skupina dobrovolníků.\nPoděkování patří hlavně: Davidovi (dakf), Kristýně Kumpánové,\nVeronice Gabrielové, Tomáši Ehrlichovi, Aničce Jaegerové,\nMatějovi Stuchlíkovi, Filipovi Sivákovi a Juraji M. Bezručkovi.", + "Pro PyLadies CZ upravil Petr Viktorin, 2015-2021." + ], + "content": { + "path": "dict/index.html" + }, + "ids": [ + "a_to_je_zatim_ke_slovnikum_vse", + "iterace", + "jak_udelat_slovnik", + "meneni_slovniku", + "slovniky", + "typy_klicu_a_hodnot", + "zapln_prazdny_slovnik" + ], + "license": "cc-by-sa-40", + "links": [ + "#a_to_je_zatim_ke_slovnikum_vse", + "#iterace", + "#jak_udelat_slovnik", + "#meneni_slovniku", + "#slovniky", + "#typy_klicu_a_hodnot", + "#zapln_prazdny_slovnik", + "https://docs.python.org/3/library/stdtypes.html#mapping-types-dict", + "https://pyvec.github.io/cheatsheets/dicts/dicts-cs.pdf" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/dict/index.md", + "title": "Slovníky", + "vars": {} + } + }, + "source_file": "lessons/beginners/dict/info.yml", + "static_files": {}, + "title": "Slovníky" + }, + "beginners/dict-with-list-values": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies CZ napsal Petr Viktorin, 2015-2021." + ], + "content": { + "path": "dict-with-list-values/index.html" + }, + "ids": [ + "vice_hodnot_vjednom_zaznamu_slovniku" + ], + "license": "cc-by-sa-40", + "links": [ + "#vice_hodnot_vjednom_zaznamu_slovniku" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/dict-with-list-values/index.md", + "title": "Víc hodnot v jednom záznamu slovníku", + "vars": {} + } + }, + "source_file": "lessons/beginners/dict-with-list-values/info.yml", + "static_files": {}, + "title": "Víc hodnot v jednom záznamu slovníku" + }, + "beginners/exceptions": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "exceptions/index.html" + }, + "ids": [ + "dalsi_prilohy_kcodetrycode", + "druhy_chyb", + "nechytej_je_vsechny", + "osetreni_chyby", + "vyjimky", + "vyvolani_chyby" + ], + "license": "cc-by-sa-40", + "links": [ + "#dalsi_prilohy_kcodetrycode", + "#druhy_chyb", + "#nechytej_je_vsechny", + "#osetreni_chyby", + "#vyjimky", + "#vyvolani_chyby", + "https://docs.python.org/3/library/exceptions.html#exception-hierarchy" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/exceptions/index.md", + "title": "Výjimky", + "vars": {} + } + }, + "source_file": "lessons/beginners/exceptions/info.yml", + "static_files": {}, + "title": "Výjimky" + }, + "beginners/expressions": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2019." + ], + "content": { + "path": "expressions/index.html" + }, + "ids": [ + "vyhodnocovani_vyrazu" + ], + "license": "cc-by-sa-40", + "links": [ + "#vyhodnocovani_vyrazu" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/expressions/index.md", + "title": "Vyhodnocování výrazů", + "vars": {} + } + }, + "source_file": "lessons/beginners/expressions/info.yml", + "static_files": {}, + "title": "Vyhodnocování výrazů" + }, + "beginners/files": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "files/index.html" + }, + "ids": [ + "automaticke_zavirani_souboru", + "iterace_nad_soubory", + "psani_souboru", + "solution-0", + "soubory" + ], + "license": "cc-by-sa-40", + "links": [ + "#automaticke_zavirani_souboru", + "#iterace_nad_soubory", + "#psani_souboru", + "#soubory", + "https://en.wikipedia.org/wiki/UTF-8", + "naucse:page?lesson=beginners/str", + "naucse:solution?solution=0" + ], + "slug": "index", + "solutions": [ + { + "content": "

Každý řádek končí znakem nového řádku, '\\n',\nkterý možná znáš ze sekce o řetězcích.\nPři procházení souboru Python tento znak nechává na konci řetězce radek ¹.\nFunkce print pak přidá další nový řádek, protože ta na konci\nvýpisu vždycky odřádkovává – pokud nedostane argument end=''.

\n
\n

¹ Proč to dělá? Kdyby '\\n' na konci řádků nebylo,\nnedalo by se např. dobře rozlišit, jestli poslední řádek\nkončí na '\\n'

" + } + ], + "source_file": "lessons/beginners/files/index.md", + "title": "Soubory", + "vars": {} + } + }, + "source_file": "lessons/beginners/files/info.yml", + "static_files": {}, + "title": "Soubory" + }, + "beginners/first-steps": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2019.", + "Část této kapitoly je založena na materiálech DjangoGirls.", + "Část této kapitoly je založena na kurzu Geek Girls Carrots.", + "Původní DjangoGirls tutoriál přeložila do češtiny skupina dobrovolníků.\nPoděkování patří hlavně: Davidovi (dakf), Kristýně Kumpánové,\nVeronice Gabrielové, Tomáši Ehrlichovi, Aničce Jaegerové,\nMatějovi Stuchlíkovi, Filipovi Sivákovi a Juraji M. Bezručkovi." + ], + "content": { + "path": "first-steps/index.html" + }, + "ids": [ + "interaktivni_rezim_pythonu", + "prvni_prikaz", + "ukonceni" + ], + "license": "cc-by-sa-40", + "links": [ + "#interaktivni_rezim_pythonu", + "#prvni_prikaz", + "#ukonceni" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/first-steps/index.md", + "title": "První krůčky", + "vars": {} + } + }, + "source_file": "lessons/beginners/first-steps/info.yml", + "static_files": {}, + "title": "První krůčky" + }, + "beginners/fstring": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2018-2019." + ], + "content": { + "path": "fstring/index.html" + }, + "ids": [ + "metoda_format", + "sablony_formatovaci_retezce" + ], + "license": "cc-by-sa-40", + "license_code": "cc0", + "links": [ + "#metoda_format", + "#sablony_formatovaci_retezce", + "https://docs.python.org/3.6/reference/lexical_analysis.html#formatted-string-literals" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/fstring/index.md", + "title": "Šablony (formátovací řetězce)", + "vars": {} + } + }, + "source_file": "lessons/beginners/fstring/info.yml", + "static_files": {}, + "title": "Šablony (formátovací řetězce)" + }, + "beginners/functions": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2019." + ], + "content": { + "path": "functions/index.html" + }, + "ids": [ + "argumenty", + "delka_retezce", + "funkce", + "funkce_je_potreba_volat", + "pojmenovane_argumenty", + "prehled_funkci", + "procedury", + "solution-0", + "volani_funkce_jako_vyraz" + ], + "license": "cc-by-sa-40", + "links": [ + "#argumenty", + "#delka_retezce", + "#funkce", + "#funkce_je_potreba_volat", + "#pojmenovane_argumenty", + "#prehled_funkci", + "#procedury", + "#volani_funkce_jako_vyraz", + "naucse:page?lesson=beginners/basic-functions", + "naucse:solution?solution=0", + "naucse:static?filename=call-anatomy.svg" + ], + "slug": "index", + "solutions": [ + { + "content": "

Funkce print zavolaná bez argumentů napíše prázdný řádek.

" + } + ], + "source_file": "lessons/beginners/functions/index.md", + "title": "Funkce", + "vars": {} + } + }, + "source_file": "lessons/beginners/functions/info.yml", + "static_files": { + "call-anatomy.svg": { + "path": "functions/call-anatomy.svg" + } + }, + "title": "Funkce" + }, + "beginners/hello-world": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "hello-world/index.html" + }, + "ids": [ + "prvni_program", + "spusteni" + ], + "license": "cc-by-sa-40", + "links": [ + "#prvni_program", + "#spusteni", + "naucse:page?lesson=beginners/cmdline", + "naucse:page?lesson=beginners/install", + "naucse:page?lesson=beginners/install-editor", + "naucse:page?lesson=beginners/venv-setup" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/hello-world/index.md", + "title": "První program", + "vars": {} + } + }, + "source_file": "lessons/beginners/hello-world/info.yml", + "static_files": {}, + "title": "První program" + }, + "beginners/inheritance": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2015-2017." + ], + "content": { + "path": "inheritance/index.html" + }, + "ids": [ + "dedicnost", + "generalizace", + "polymorfismus", + "prepisovani_metod_a_codesupercode" + ], + "license": "cc-by-sa-40", + "links": [ + "#dedicnost", + "#generalizace", + "#polymorfismus", + "#prepisovani_metod_a_codesupercode", + "https://en.wikipedia.org/wiki/Liskov_substitution_principle" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/inheritance/index.md", + "title": "Dědičnost", + "vars": {} + } + }, + "source_file": "lessons/beginners/inheritance/info.yml", + "static_files": {}, + "title": "Dědičnost" + }, + "beginners/install": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Martin Pavlásek (2023)" + ], + "content": { + "path": "install/index.html" + }, + "ids": [ + "instalace_pythonu" + ], + "license": "cc-by-sa-40", + "links": [ + "#instalace_pythonu", + "naucse:page?lesson=beginners/install&page=linux", + "naucse:page?lesson=beginners/install&page=macos", + "naucse:page?lesson=beginners/install&page=windows" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/install/index.md", + "title": "Instalace Pythonu", + "vars": {} + }, + "linux": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Martin Pavlásek (2023)" + ], + "content": { + "path": "install/linux.html" + }, + "ids": [ + "check-tkinter", + "doinstalovani_virtualenv", + "instalace_pythonu_3", + "instalace_pythonu_na_linux", + "install-virtualenv", + "kontrola_tkinter" + ], + "license": "cc-by-sa-40", + "links": [ + "#check-tkinter", + "#doinstalovani_virtualenv", + "#instalace_pythonu_3", + "#instalace_pythonu_na_linux", + "#install-virtualenv", + "#kontrola_tkinter", + "naucse:page?lesson=beginners/cmdline" + ], + "slug": "linux", + "solutions": [], + "source_file": "lessons/beginners/install/linux.md", + "subtitle": "Linux", + "title": "Instalace Pythonu – Linux", + "vars": {} + }, + "macos": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Martin Pavlásek (2023)" + ], + "content": { + "path": "install/macos.html" + }, + "ids": [ + "instalace_pythonu_pro_macos" + ], + "license": "cc-by-sa-40", + "links": [ + "#instalace_pythonu_pro_macos", + "http://brew.sh", + "naucse:page?lesson=beginners/cmdline" + ], + "slug": "macos", + "solutions": [], + "source_file": "lessons/beginners/install/macos.md", + "subtitle": "macOS", + "title": "Instalace Pythonu – macOS", + "vars": {} + }, + "windows": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Martin Pavlásek (2023)" + ], + "content": { + "path": "install/windows.html" + }, + "ids": [ + "download", + "instalace", + "instalace_pythonu_pro_windows", + "stazeni", + "zjisti_jestli_uz_mas_python_nainstalovany" + ], + "license": "cc-by-sa-40", + "links": [ + "#download", + "#instalace", + "#instalace_pythonu_pro_windows", + "#stazeni", + "#zjisti_jestli_uz_mas_python_nainstalovany", + "https://www.python.org/downloads/windows/", + "naucse:page?lesson=beginners/cmdline", + "naucse:page?lesson=beginners/venv-setup", + "naucse:static?filename=windows_32v64-bit.png", + "naucse:static?filename=windows_add_python_to_path.png" + ], + "slug": "windows", + "solutions": [], + "source_file": "lessons/beginners/install/windows.md", + "subtitle": "Windows", + "title": "Instalace Pythonu – Windows", + "vars": {} + } + }, + "source_file": "lessons/beginners/install/info.yml", + "static_files": { + "windows_32v64-bit.png": { + "path": "install/windows_32v64-bit.png" + }, + "windows_add_python_to_path.png": { + "path": "install/windows_add_python_to_path.png" + } + }, + "title": "Instalace Pythonu" + }, + "beginners/install-editor": { + "pages": { + "atom": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Tomáš Roj (2019)" + ], + "content": { + "path": "install-editor/atom.html" + }, + "ids": [ + "instalace_atomu", + "kontrola_stylu_zdrojoveho_kodu", + "nacvik_odsazovani", + "nastaveni" + ], + "license": "cc-by-sa-40", + "links": [ + "#instalace_atomu", + "#kontrola_stylu_zdrojoveho_kodu", + "#nacvik_odsazovani", + "#nastaveni", + "https://atom.io", + "https://www.python.org/dev/peps/pep-0008/" + ], + "slug": "atom", + "solutions": [], + "source_file": "lessons/beginners/install-editor/atom.md", + "subtitle": "Atom", + "title": "Instalace Atomu", + "vars": {} + }, + "gedit": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Tomáš Roj (2019)" + ], + "content": { + "path": "install-editor/gedit.html" + }, + "ids": [ + "instalace_geditu", + "nacvik_odsazovani", + "nastaveni" + ], + "license": "cc-by-sa-40", + "links": [ + "#instalace_geditu", + "#nacvik_odsazovani", + "#nastaveni", + "https://wiki.gnome.org/Apps/Gedit", + "naucse:static?filename=gedit_indent.png", + "naucse:static?filename=gedit_linenums.png", + "naucse:static?filename=gedit_prefs.png" + ], + "slug": "gedit", + "solutions": [], + "source_file": "lessons/beginners/install-editor/gedit.md", + "subtitle": "Gedit", + "title": "Instalace Geditu", + "vars": {} + }, + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Tomáš Roj (2019)" + ], + "content": { + "path": "install-editor/index.html" + }, + "ids": [ + "co_programatorsky_editor_umi", + "ide", + "instalace_editoru", + "volba_a_nastaveni_editoru" + ], + "license": "cc-by-sa-40", + "links": [ + "#co_programatorsky_editor_umi", + "#ide", + "#instalace_editoru", + "#volba_a_nastaveni_editoru", + "https://eclipse.org/", + "https://www.jetbrains.com/pycharm/", + "https://www.kdevelop.org/", + "naucse:page?lesson=beginners/install-editor&page=atom", + "naucse:page?lesson=beginners/install-editor&page=gedit", + "naucse:page?lesson=beginners/install-editor&page=kate", + "naucse:page?lesson=beginners/install-editor&page=notepad-plus-plus", + "naucse:page?lesson=beginners/install-editor&page=others", + "naucse:page?lesson=beginners/install-editor&page=vscode" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/install-editor/index.md", + "title": "Instalace editoru", + "vars": {} + }, + "kate": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Tomáš Roj (2019)" + ], + "content": { + "path": "install-editor/kate.html" + }, + "ids": [ + "instalace_kate", + "nacvik_odsazovani", + "nastaveni" + ], + "license": "cc-by-sa-40", + "links": [ + "#instalace_kate", + "#nacvik_odsazovani", + "#nastaveni", + "https://kate-editor.org/get-it/" + ], + "slug": "kate", + "solutions": [], + "source_file": "lessons/beginners/install-editor/kate.md", + "subtitle": "Kate", + "title": "Instalace Kate", + "vars": {} + }, + "notepad-plus-plus": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Tomáš Roj (2019)" + ], + "content": { + "path": "install-editor/notepad-plus-plus.html" + }, + "ids": [ + "instalace_notepadu", + "nacvik_odsazovani", + "nastaveni" + ], + "license": "cc-by-sa-40", + "links": [ + "#instalace_notepadu", + "#nacvik_odsazovani", + "#nastaveni", + "https://notepad-plus-plus.org/" + ], + "slug": "notepad-plus-plus", + "solutions": [], + "source_file": "lessons/beginners/install-editor/notepad-plus-plus.md", + "subtitle": "Notepad++", + "title": "Instalace Notepadu++", + "vars": {} + }, + "others": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Tomáš Roj (2019)" + ], + "content": { + "path": "install-editor/others.html" + }, + "ids": [ + "cislovani_radku", + "instalace_editoru", + "kontrola_stylu_zdrojoveho_kodu", + "nacvik_odsazovani", + "nastaveni", + "obarvovani", + "odsazovani" + ], + "license": "cc-by-sa-40", + "links": [ + "#cislovani_radku", + "#instalace_editoru", + "#kontrola_stylu_zdrojoveho_kodu", + "#nacvik_odsazovani", + "#nastaveni", + "#obarvovani", + "#odsazovani", + "https://www.python.org/dev/peps/pep-0008/" + ], + "slug": "others", + "solutions": [], + "source_file": "lessons/beginners/install-editor/others.md", + "subtitle": "Nastavení", + "title": "Nastavení editoru", + "vars": {} + }, + "vscode": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017, Tomáš Roj (2019)" + ], + "content": { + "path": "install-editor/vscode.html" + }, + "ids": [ + "instalace_visual_studio_code", + "nacvik_odsazovani", + "nastaveni", + "odesilani_telemetrickych_dat", + "stazeni_a_instalace" + ], + "license": "cc-by-sa-40", + "links": [ + "#instalace_visual_studio_code", + "#nacvik_odsazovani", + "#nastaveni", + "#odesilani_telemetrickych_dat", + "#stazeni_a_instalace", + "https://code.visualstudio.com/", + "https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting", + "https://privacy.microsoft.com/en-us/privacystatement" + ], + "slug": "vscode", + "solutions": [], + "source_file": "lessons/beginners/install-editor/vscode.md", + "subtitle": "Visual Studio Code", + "title": "Instalace Visual Studio Code", + "vars": {} + } + }, + "source_file": "lessons/beginners/install-editor/info.yml", + "static_files": { + "gedit_indent.png": { + "path": "install-editor/gedit_indent.png" + }, + "gedit_linenums.png": { + "path": "install-editor/gedit_linenums.png" + }, + "gedit_prefs.png": { + "path": "install-editor/gedit_prefs.png" + } + }, + "title": "Instalace editoru" + }, + "beginners/interfaces": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2020." + ], + "content": { + "path": "interfaces/index.html" + }, + "ids": [ + "rozhrani", + "rozhrani_funkce", + "vyzkousej_si_to" + ], + "license": "cc-by-sa-40", + "links": [ + "#rozhrani", + "#rozhrani_funkce", + "#vyzkousej_si_to", + "naucse:static?filename=tkui.py", + "naucse:static?filename=yn.png" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/interfaces/index.md", + "title": "Rozhraní", + "vars": {} + } + }, + "source_file": "lessons/beginners/interfaces/info.yml", + "static_files": { + "tkui.py": { + "path": "interfaces/tkui.py" + }, + "yn.png": { + "path": "interfaces/yn.png" + } + }, + "title": "Rozhraní" + }, + "beginners/list": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "list/index.html" + }, + "ids": [ + "dalsi_zpusoby_jak_menit_seznamy", + "mazani_prvku", + "meneni_prvku", + "meneni_seznamu", + "nemenitelne_hodnoty", + "razeni", + "seznam_jako_podminka", + "seznamy", + "seznamy_a_nahoda", + "seznamy_a_retezce", + "tvoreni_seznamu", + "vybirani_ze_seznamu", + "zname_operace_se_seznamy" + ], + "license": "cc-by-sa-40", + "links": [ + "#dalsi_zpusoby_jak_menit_seznamy", + "#mazani_prvku", + "#meneni_prvku", + "#meneni_seznamu", + "#nemenitelne_hodnoty", + "#razeni", + "#seznam_jako_podminka", + "#seznamy", + "#seznamy_a_nahoda", + "#seznamy_a_retezce", + "#tvoreni_seznamu", + "#vybirani_ze_seznamu", + "#zname_operace_se_seznamy", + "https://github.com/pyvec/cheatsheets/blob/master/lists/lists-cs.pdf", + "naucse:page?lesson=beginners/str-index-slice#slicing-diagram", + "naucse:static?filename=methods.svg" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/list/index.md", + "title": "Seznamy", + "vars": {} + } + }, + "source_file": "lessons/beginners/list/info.yml", + "static_files": { + "methods.svg": { + "path": "list/methods.svg" + } + }, + "title": "Seznamy" + }, + "beginners/local-variables": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "local-variables/index.html" + }, + "ids": [ + "lokalni_nebo_globalni", + "lokalni_promenne", + "prirazeni", + "rada_na_zaver", + "skryvani_detailu", + "solution-0", + "zakryvani_jmena" + ], + "license": "cc-by-sa-40", + "links": [ + "#lokalni_nebo_globalni", + "#lokalni_promenne", + "#prirazeni", + "#rada_na_zaver", + "#skryvani_detailu", + "#zakryvani_jmena", + "naucse:solution?solution=0" + ], + "slug": "index", + "solutions": [ + { + "content": "
    \n
  • pi je globální – nepřiřazuje se do ní ve funkci;\nje „vidět“ v celém programu.
  • \n
  • Proměnné obsah jsou v programu dvě – jedna globální,\na jedna je lokální pro funkci obsah_elipsy,\nprotože do ní tahle funkce přiřazuje.
  • \n
  • Proměnné a jsou taky dvě, podobně jako obsah.\nTady byl chyták: příkaz a = a + 3 nemá žádný smysl;\ndo a se sice uloží větší číslo, ale vzápětí funkce obsah_elipsy skončí\na její lokální proměnná a přestane existovat.
  • \n
  • Proměnná b je jenom lokální – jako parametr funkce obsah_elipsy.
  • \n
  • Proměnná obsah_elipsy je globální (a je v ní funkce).
  • \n
\n

A pro úplnost

\n
    \n
  • Klíčová slova from, import, def, return neoznačují proměnné.
  • \n
  • Jméno modulu math taky neoznačuje proměnnou.
  • \n
  • Proměnná print se dá považovat za globální.\n(Ve skutečnosti existuje zvláštní kategorie zabudovaných (angl. builtin)\nproměnných – ty jsou „ještě globálnější“.)
  • \n
\n
" + } + ], + "source_file": "lessons/beginners/local-variables/index.md", + "title": "Lokální proměnné", + "vars": {} + } + }, + "source_file": "lessons/beginners/local-variables/info.yml", + "static_files": {}, + "title": "Lokální proměnné" + }, + "beginners/main-module": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2020." + ], + "content": { + "path": "main-module/index.html" + }, + "ids": [ + "negativni_testy", + "spousteci_moduly" + ], + "license": "cc-by-sa-40", + "links": [ + "#negativni_testy", + "#spousteci_moduly" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/main-module/index.md", + "title": "Spouštěcí moduly a Negativní testy", + "vars": {} + } + }, + "source_file": "lessons/beginners/main-module/info.yml", + "static_files": {}, + "title": "Spouštěcí moduly a Negativní testy" + }, + "beginners/micropython": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2016-2017.", + "Diagramy s LED vytvořeny pomocí Fritzing." + ], + "content": { + "path": "micropython/index.html" + }, + "css": ".part-green { outline: 2px solid hsla(113, 100%, 50%, 1);\n background-color: hsla(113, 100%, 50%, 0.25); }\n.part-cyan { outline: 2px solid hsla(180, 100%, 50%, 1);\n background-color: hsla(180, 100%, 50%, 0.25); }\n.part-blue { outline: 2px solid hsla(236, 100%, 50%, 1);\n background-color: hsla(236, 100%, 50%, 0.25); }\n.part-yellow { outline: 2px solid hsla( 60, 100%, 50%, 1);\n background-color: hsla( 60, 100%, 50%, 0.25); }\n.part-orange { outline: 2px solid hsla( 42, 100%, 50%, 1);\n background-color: hsla( 42, 100%, 50%, 0.25); }\n.part-red { outline: 2px solid hsla( 0, 100%, 50%, 1);\n background-color: hsla( 0, 100%, 50%, 0.25); }\n.pull-right, .pull-left { margin: 1em; }\n.highlight { background-color: hsla( 0, 100%, 50%, 0.1); }\n.img-fluid { max-width: 100%; }\n.highlight-nocolor{ background-color: hsla( 60, 100%, 50%, 0.75); }\n.highlight-red { background-color: hsla( 0, 100%, 50%, 0.25); }\n.highlight-green { background-color: hsla(113, 100%, 50%, 0.25); }\n.highlight-blue { background-color: hsla(236, 100%, 50%, 0.25); }\n", + "ids": [ + "barevna_svetylka", + "dalsi_ovladani", + "instalace", + "linux", + "macos", + "micropython_na_malem_zarizeni", + "micropython_taky_python", + "napeti", + "obvod", + "pousteni_kodu_ze_souboru", + "poznamka_o_napajeni_a_napeti", + "rotace", + "solution-0", + "tony_a_melodie", + "velice_rychle_blikat", + "vstup", + "vystup", + "windows" + ], + "license": "cc-by-sa-40", + "links": [ + "#barevna_svetylka", + "#dalsi_ovladani", + "#instalace", + "#linux", + "#macos", + "#micropython_na_malem_zarizeni", + "#micropython_taky_python", + "#napeti", + "#obvod", + "#pousteni_kodu_ze_souboru", + "#poznamka_o_napajeni_a_napeti", + "#rotace", + "#tony_a_melodie", + "#velice_rychle_blikat", + "#vstup", + "#vystup", + "#windows", + "http://docs.micropython.org/en/latest/pyboard/pyboard/tutorial/repl.html", + "http://kig.re/2014/12/31/how-to-use-arduino-nano-mini-pro-with-CH340G-on-mac-osx-yosemite.html", + "http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html", + "https://en.wikipedia.org/wiki/Hertz", + "https://en.wikipedia.org/wiki/UART", + "https://onedrive.live.com/?authkey=%21AKLO1lLajaeTmo4&id=182EF9BC3A8BDDA4%2166743&cid=182EF9BC3A8BDDA4", + "https://pyvec.github.io/cheatsheets/micropython/nodemcu-cs.pdf", + "https://tzapu.com/ch340-ch341-serial-adapters-macos-sierra/", + "naucse:solution?solution=0", + "naucse:static?filename=circuits/led_bb.svg", + "naucse:static?filename=circuits/led_bb_off.svg", + "naucse:static?filename=nodemcu-devkit.jpg", + "naucse:static?filename=nodemcu-popisky.svg", + "naucse:static?filename=noty.py", + "naucse:static?filename=putty-config.jpg", + "naucse:static?filename=usb-ttl.png" + ], + "slug": "index", + "solutions": [ + { + "content": "
from machine import Pin\npin_diody = Pin(14, Pin.OUT)\npin_tlacitka = Pin(0, Pin.IN)\nwhile True:\n    pin_diody.value(1 - pin_tlacitka.value())\n
" + } + ], + "source_file": "lessons/beginners/micropython/index.md", + "title": "MicroPython", + "vars": {} + } + }, + "source_file": "lessons/beginners/micropython/info.yml", + "static_files": { + "circuits/led.fzz": { + "path": "micropython/circuits/led.fzz" + }, + "circuits/led_bb.svg": { + "path": "micropython/circuits/led_bb.svg" + }, + "circuits/led_bb_off.svg": { + "path": "micropython/circuits/led_bb_off.svg" + }, + "nodemcu-devkit.jpg": { + "path": "micropython/nodemcu-devkit.jpg" + }, + "nodemcu-popisky.svg": { + "path": "micropython/nodemcu-popisky.svg" + }, + "noty.py": { + "path": "micropython/noty.py" + }, + "putty-config.jpg": { + "path": "micropython/putty-config.jpg" + }, + "usb-ttl.png": { + "path": "micropython/usb-ttl.png" + } + }, + "title": "MicroPython" + }, + "beginners/modules": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "modules/index.html" + }, + "ids": [ + "adresar_pro_kazdy_projekt", + "moduly", + "vedlejsi_efekty", + "vlastni_moduly" + ], + "license": "cc-by-sa-40", + "links": [ + "#adresar_pro_kazdy_projekt", + "#moduly", + "#vedlejsi_efekty", + "#vlastni_moduly" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/modules/index.md", + "title": "Moduly", + "vars": {} + } + }, + "source_file": "lessons/beginners/modules/info.yml", + "static_files": {}, + "title": "Moduly" + }, + "beginners/nested-list": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2020." + ], + "content": { + "path": "nested-list/index.html" + }, + "ids": [ + "vnorene_seznamy" + ], + "license": "cc-by-sa-40", + "links": [ + "#vnorene_seznamy" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/nested-list/index.md", + "title": "Vnořené seznamy", + "vars": {} + } + }, + "source_file": "lessons/beginners/nested-list/info.yml", + "static_files": {}, + "title": "Vnořené seznamy" + }, + "beginners/nested-traceback": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "nested-traceback/index.html" + }, + "ids": [ + "vypisy_chyb_ze_zanorenych_funkci" + ], + "license": "cc-by-sa-40", + "links": [ + "#vypisy_chyb_ze_zanorenych_funkci" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/nested-traceback/index.md", + "title": "Chybové hlášky ze zanořených funkcí", + "vars": {} + } + }, + "source_file": "lessons/beginners/nested-traceback/info.yml", + "static_files": {}, + "title": "Chybové hlášky ze zanořených funkcí" + }, + "beginners/prefer-return": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "prefer-return/index.html" + }, + "ids": [ + "none", + "vratit_nebo_vypsat" + ], + "license": "cc-by-sa-40", + "links": [ + "#none", + "#vratit_nebo_vypsat" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/prefer-return/index.md", + "title": "Vrátit nebo vypsat?", + "vars": {} + } + }, + "source_file": "lessons/beginners/prefer-return/info.yml", + "static_files": {}, + "title": "Vrátit nebo vypsat?" + }, + "beginners/print": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "print/index.html" + }, + "css": ".err-lineno {\n display: inline-block;\n background-color: #FCC;\n}\n.err-exctype {\n display: inline-block;\n background-color: #CFC;\n}\n", + "ids": [ + "dalsi_prikazy", + "jak_cist_chyby", + "jak_funguje_program", + "print_a_chybove_hlasky", + "print_a_vyrazy" + ], + "license": "cc-by-sa-40", + "links": [ + "#dalsi_prikazy", + "#jak_cist_chyby", + "#jak_funguje_program", + "#print_a_chybove_hlasky", + "#print_a_vyrazy", + "naucse:page?lesson=beginners/variables" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/print/index.md", + "title": "Print a chybové hlášky", + "vars": {} + } + }, + "source_file": "lessons/beginners/print/info.yml", + "static_files": {}, + "title": "Print a chybové hlášky" + }, + "beginners/range": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2019." + ], + "content": { + "path": "range/index.html" + }, + "ids": [ + "range_sekvence_cisel" + ], + "license": "cc-by-sa-40", + "links": [ + "#range_sekvence_cisel" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/range/index.md", + "title": "Range - sekvence čísel", + "vars": {} + } + }, + "source_file": "lessons/beginners/range/info.yml", + "static_files": {}, + "title": "Range - sekvence čísel" + }, + "beginners/reassignment": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2019." + ], + "content": { + "path": "reassignment/index.html" + }, + "ids": [ + "prepisovani_promennych", + "prepisovani_vcyklu", + "solution-0" + ], + "license": "cc-by-sa-40", + "links": [ + "#prepisovani_promennych", + "#prepisovani_vcyklu", + "naucse:solution?solution=0" + ], + "slug": "index", + "solutions": [ + { + "content": "
celkem = 0\n\ndelka_trasy = 8\nprint('Jdu', delka_trasy, 'km do další vesnice.')\ncelkem = celkem + delka_trasy\n\ndelka_trasy = 45\nprint('Jdu', delka_trasy, 'km do další vesnice.')\ncelkem = celkem + delka_trasy\n\ndelka_trasy = 9\nprint('Jdu', delka_trasy, 'km do další vesnice.')\ncelkem = celkem + delka_trasy\n\ndelka_trasy = 21\nprint('Jdu', delka_trasy, 'km do další vesnice.')\ncelkem = celkem + delka_trasy\n\nprint('Celkem jsem ušla', celkem, 'km')\n

Příkaz celkem = celkem + delka_trasy vypočítá hodnotu\ncelkem + delka_trasy, tedy přičte aktuální číslo k součtu.\nVýsledek uloží opět do proměnné celkem.\nNová hodnota celkem se pak použije v dalším průchodu cyklem.

\n

Na začátku je celkem 0 a na konci se celkový součet všech čísel vypíše.

" + } + ], + "source_file": "lessons/beginners/reassignment/index.md", + "title": "Přepisování proměnných", + "vars": {} + } + }, + "source_file": "lessons/beginners/reassignment/info.yml", + "static_files": {}, + "title": "Přepisování proměnných" + }, + "beginners/recursion": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2019." + ], + "content": { + "path": "recursion/index.html" + }, + "ids": [ + "kontrolovane_zanoreni", + "lokalni_promenne", + "pro_matematiky", + "rekurze" + ], + "license": "cc-by-sa-40", + "links": [ + "#kontrolovane_zanoreni", + "#lokalni_promenne", + "#pro_matematiky", + "#rekurze" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/recursion/index.md", + "title": "Rekurze", + "vars": {} + } + }, + "source_file": "lessons/beginners/recursion/info.yml", + "static_files": {}, + "title": "Rekurze" + }, + "beginners/str": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2019.", + "Jako jeden z příkladů byl použit úryvek z povídky „Chlap, děd, vnuk, pes a hrob“ Jana Wericha." + ], + "content": { + "path": "str/index.html" + }, + "ids": [ + "cviceni", + "novy_radek", + "sekvence_se_zpetnym_lomitkem", + "solution-0", + "trojite_uvozovky", + "uvozovky", + "uvozovky_vuvozovkach", + "zapis_retezcu", + "znaky", + "zpetne_lomitko" + ], + "license": "cc-by-sa-40", + "links": [ + "#cviceni", + "#novy_radek", + "#sekvence_se_zpetnym_lomitkem", + "#trojite_uvozovky", + "#uvozovky", + "#uvozovky_vuvozovkach", + "#zapis_retezcu", + "#znaky", + "#zpetne_lomitko", + "naucse:solution?solution=0", + "naucse:static?filename=quote-comic.svg" + ], + "slug": "index", + "solutions": [ + { + "content": "
>>> print(".\\".")\n.".\n>>> len(".\\".")\n3\n>>> ".\\"."\n'.".'\n
" + } + ], + "source_file": "lessons/beginners/str/index.md", + "title": "Zápis řetězců", + "vars": {} + } + }, + "source_file": "lessons/beginners/str/info.yml", + "static_files": { + "quote-comic.svg": { + "path": "str/quote-comic.svg" + } + }, + "title": "Zápis řetězců" + }, + "beginners/str-index-slice": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2019." + ], + "content": { + "path": "str-index-slice/index.html" + }, + "ids": [ + "cviceni", + "sekani_retezcu", + "slicing-diagram", + "solution-0", + "solution-1", + "solution-2", + "solution-3", + "solution-4", + "vyber_znaku", + "vyber_zretezcu" + ], + "license": "cc-by-sa-40", + "links": [ + "#cviceni", + "#sekani_retezcu", + "#vyber_znaku", + "#vyber_zretezcu", + "naucse:solution?solution=0", + "naucse:solution?solution=1", + "naucse:solution?solution=2", + "naucse:solution?solution=3", + "naucse:solution?solution=4" + ], + "slug": "index", + "solutions": [ + { + "content": "

Nedostal/a – dostal/a jsi šesté písmeno.

" + }, + { + "content": "
print('Čokoláda'[-1])  # → a\nprint('Čokoláda'[-2])  # → d\nprint('Čokoláda'[-3])  # → á\nprint('Čokoláda'[-4])  # → l\n

Záporná čísla vybírají písmenka od konce.

\n
   [0] [1] [2] [3] [4] [5] [6] [7]\n   [-8][-7][-6][-5][-4][-3][-2][-1]\n  ╭───┬───┬───┬───┬───┬───┬───┬───╮\n  │ Č │ o │ k │ o │ l │ á │ d │ a │\n  ╰───┴───┴───┴───┴───┴───┴───┴───╯\n
" + }, + { + "content": "

Zápis retezec[5:] vybere podřetězec od znaku číslo 5 dál.

" + }, + { + "content": "

Zápis retezec[od:do] vybere podřetězec od pozice od do pozice do.\nKdyž jednu z hodnot vynecháš, vybírá se od začátku, resp. do konce.

\n
retezec = 'čokoláda'\nprint(retezec[:4])      # → čoko\nprint(retezec[2:6])     # → kolá\nprint(retezec[-3:])     # → áda\nprint(retezec[:])       # → čokoláda\n
" + }, + { + "content": "
slovo = input('Slovo: ')\npozice = int(input('Které písmeno zaměnit (od nuly)? '))\nnovy_znak = input('Nové písmeno: ')\n\nzacatek_slova = slovo[:pozice]\nkonec_slova = slovo[pozice + 1:]\nnove_slovo = zacatek_slova + novy_znak + konec_slova\n\nprint(nove_slovo)\n
" + } + ], + "source_file": "lessons/beginners/str-index-slice/index.md", + "title": "Výběr z řetězců", + "vars": {} + } + }, + "source_file": "lessons/beginners/str-index-slice/info.yml", + "static_files": {}, + "title": "Výběr z řetězců" + }, + "beginners/str-methods": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2019." + ], + "content": { + "path": "str-methods/index.html" + }, + "ids": [ + "a_dalsi", + "inicialy", + "metody", + "retezcove_funkce_a_metody", + "solution-0" + ], + "license": "cc-by-sa-40", + "links": [ + "#a_dalsi", + "#inicialy", + "#metody", + "#retezcove_funkce_a_metody", + "https://docs.python.org/3/library/stdtypes.html#string-methods", + "https://pyvec.github.io/cheatsheets/strings/strings-cs.pdf", + "naucse:solution?solution=0" + ], + "slug": "index", + "solutions": [ + { + "content": "
jmeno = input('Zadej jméno: ')\nprijmeni = input('Zadej příjmení ')\ninicialy = jmeno[0] + prijmeni[0]\nprint('Iniciály:', inicialy.upper())\n

Způsobů, jak takový program napsat, je více.\nLze například zavolat upper() dvakrát – zvlášť na jméno a zvlášť na příjmení.

\n

Nebo to jde zapsat i takto –\nmetoda se dá volat na výsledku jakéhokoli výrazu:

\n
jmeno = input('Zadej jméno: ')\nprijmeni = input('Zadej příjmení ')\nprint('Iniciály:', (jmeno[0] + prijmeni[0]).upper())\n

Doporučuji spíš první způsob, ten se smysluplnými názvy proměnných.\nJe sice delší, ale mnohem přehlednější.

" + } + ], + "source_file": "lessons/beginners/str-methods/index.md", + "title": "Řetězcové funkce a metody", + "vars": {} + } + }, + "source_file": "lessons/beginners/str-methods/info.yml", + "static_files": {}, + "title": "Řetězcové funkce a metody" + }, + "beginners/testing": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2020." + ], + "content": { + "path": "testing/index.html" + }, + "ids": [ + "instalace_knihovny_pytest", + "psani_testu", + "spousteni_testu", + "testovaci_moduly", + "testovani" + ], + "license": "cc-by-sa-40", + "links": [ + "#instalace_knihovny_pytest", + "#psani_testu", + "#spousteni_testu", + "#testovaci_moduly", + "#testovani", + "naucse:page?lesson=beginners/install" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/testing/index.md", + "title": "Testování", + "vars": {} + } + }, + "source_file": "lessons/beginners/testing/info.yml", + "static_files": {}, + "title": "Testování" + }, + "beginners/tuple": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "tuple/index.html" + }, + "ids": [ + "kdy_pouzit_seznam_a_kdy_varnvar-tici", + "male_varnvar-tice", + "varnvar-tice" + ], + "license": "cc-by-sa-40", + "links": [ + "#kdy_pouzit_seznam_a_kdy_varnvar-tici", + "#male_varnvar-tice", + "#varnvar-tice" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/tuple/index.md", + "title": "N-tice", + "vars": {} + } + }, + "source_file": "lessons/beginners/tuple/info.yml", + "static_files": {}, + "title": "N-tice" + }, + "beginners/variables": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "variables/index.html" + }, + "ids": [ + "ctverec", + "komentare", + "kruhy", + "mensi_ctverec", + "nacitani_vstupu", + "promenne", + "solution-0", + "solution-1" + ], + "license": "cc-by-sa-40", + "links": [ + "#ctverec", + "#komentare", + "#kruhy", + "#mensi_ctverec", + "#nacitani_vstupu", + "#promenne", + "naucse:page?lesson=beginners/print", + "naucse:solution?solution=0", + "naucse:solution?solution=1" + ], + "slug": "index", + "solutions": [ + { + "content": "

Program, který vypíše správný výsledek, může vypadat třeba takhle:

\n
print('Obvod čtverce se stranou 356 cm je', 4 * 356, 'cm')\nprint('Obsah čtverce se stranou 356 cm je', 356 * 356, 'cm2')\n
" + }, + { + "content": "
print('Obvod čtverce se stranou 123 cm je', 4 * 123, 'cm')\nprint('Obsah čtverce se stranou 123 cm je', 123 * 123, 'cm2')\n
" + } + ], + "source_file": "lessons/beginners/variables/index.md", + "title": "Proměnné", + "vars": {} + } + }, + "source_file": "lessons/beginners/variables/info.yml", + "static_files": {}, + "title": "Proměnné" + }, + "beginners/venv-setup": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2018." + ], + "content": { + "path": "venv-setup/index.html" + }, + "ids": [ + "adresar_pro_kazdou_lekci", + "aktivace_virtualniho_prostredi", + "nastaveni_prostredi", + "prepnuti", + "priprava_adresare", + "virtualni_prostredi" + ], + "license": "cc-by-sa-40", + "links": [ + "#adresar_pro_kazdou_lekci", + "#aktivace_virtualniho_prostredi", + "#nastaveni_prostredi", + "#prepnuti", + "#priprava_adresare", + "#virtualni_prostredi", + "naucse:static?filename=dirs-00.png" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/venv-setup/index.md", + "title": "Nastavení prostředí", + "vars": {} + } + }, + "source_file": "lessons/beginners/venv-setup/info.yml", + "static_files": { + "dirs-00.png": { + "path": "venv-setup/dirs-00.png" + } + }, + "title": "Nastavení prostředí" + }, + "beginners/while": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "while/index.html" + }, + "ids": [ + "oko_bere", + "solution-0", + "while" + ], + "license": "cc-by-sa-40", + "links": [ + "#oko_bere", + "#while", + "naucse:solution?solution=0" + ], + "slug": "index", + "solutions": [ + { + "content": "
from random import randrange\n\nsoucet = 0\nwhile soucet < 21:\n    print('Máš', soucet, 'bodů')\n    odpoved = input('Otočit kartu? ')\n    if odpoved == 'ano':\n        karta = randrange(2, 11)\n        print('Otočil/a jsi', karta)\n        soucet = soucet + karta\n    elif odpoved == 'ne':\n        break\n    else:\n        print('Nerozumím! Odpovídej "ano", nebo "ne"')\n\nif soucet == 21:\n    print('Gratuluji! Vyhrál/a jsi!')\nelif soucet > 21:\n    print('Smůla!', soucet, 'bodů je moc!')\nelse:\n    print('Chybělo jen', 21 - soucet, 'bodů!')\n
" + } + ], + "source_file": "lessons/beginners/while/index.md", + "title": "Cyklus While", + "vars": {} + } + }, + "source_file": "lessons/beginners/while/info.yml", + "static_files": {}, + "title": "Cyklus While" + }, + "beginners/with": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2021." + ], + "content": { + "path": "with/index.html" + }, + "ids": [ + "kontext_codewithcode_a_codefinallycode", + "kontrola_vyjimek" + ], + "license": "cc-by-sa-40", + "links": [ + "#kontext_codewithcode_a_codefinallycode", + "#kontrola_vyjimek" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/beginners/with/index.md", + "title": "Kontext: with a finally", + "vars": {} + } + }, + "source_file": "lessons/beginners/with/info.yml", + "static_files": {}, + "title": "Kontext: with a finally" + }, + "beginners/zip-enumerate": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2019." + ], + "content": { + "path": "zip-enumerate/index.html" + }, + "ids": [ + "enumerate_ocislovani_sekvence", + "iteratory_n-tic", + "rozbalovani_v_cyklu_for", + "solution-0", + "solution-1", + "zip_longest_pro_ty_co_chteji_vsechno", + "zip_vic_iteraci_najednou" + ], + "license": "cc-by-sa-40", + "links": [ + "#enumerate_ocislovani_sekvence", + "#iteratory_n-tic", + "#rozbalovani_v_cyklu_for", + "#zip_longest_pro_ty_co_chteji_vsechno", + "#zip_vic_iteraci_najednou", + "naucse:solution?solution=0", + "naucse:solution?solution=1" + ], + "slug": "index", + "solutions": [ + { + "content": "
dny = ['Po', 'Út', 'St', 'Čt', 'Pá', 'So', 'Ne']\nfor index, den in enumerate(dny):\n    cislo = index + 1\n    print(f'{cislo}. {den}')\n

To je trošku kostrbaté, ale dá se to zjednodušit: buď jako\nf'{index + 1}. {den}', nebo můžeš funkci enumerate předat\npojmenovaný argument start, pomocí kterého umí sama\npočítat od jiného začátku než od nuly:

\n
dny = ['Po', 'Út', 'St', 'Čt', 'Pá', 'So', 'Ne']\nfor index, den in enumerate(dny, start=1):\n    print(f'{index}. {den}')\n
" + }, + { + "content": "

Iterátor zip skončí hned, když dojdou prvky nejkratší sekvence.

" + } + ], + "source_file": "lessons/beginners/zip-enumerate/index.md", + "title": "Iterátory n-tic", + "vars": {} + } + }, + "source_file": "lessons/beginners/zip-enumerate/info.yml", + "static_files": {}, + "title": "Iterátory n-tic" + }, + "fast-track/http": { + "pages": { + "index": { + "attribution": [ + "Pro naucse.python.cz napsal Petr Viktorin, 2018.", + "Inspirováno tutoriálem Django Girls" + ], + "content": { + "path": "http/index.html" + }, + "ids": [ + "http_jak_funguje_internet", + "http_metody", + "pozadavek_a_odpoved", + "url-anatomy", + "webove_adresy" + ], + "license": "cc-by-sa-40", + "links": [ + "#http_jak_funguje_internet", + "#http_metody", + "#pozadavek_a_odpoved", + "#webove_adresy", + "https://en.wikipedia.org/wiki/List_of_HTTP_status_codes", + "https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html", + "naucse:static?filename=url-anatomy.svg" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/fast-track/http/index.md", + "title": "HTTP – Jak funguje Internet", + "vars": {} + } + }, + "source_file": "lessons/fast-track/http/info.yml", + "static_files": { + "url-anatomy.svg": { + "path": "http/url-anatomy.svg" + } + }, + "title": "HTTP – Jak funguje Internet" + }, + "fast-track/install": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Miro Hrončok a Petr Viktorin, 2016-2017." + ], + "content": { + "path": "install/index.1.html" + }, + "ids": [ + "poznamky", + "unix_linux_macos", + "windows" + ], + "license": "cc-by-sa-40", + "links": [ + "#poznamky", + "#unix_linux_macos", + "#windows", + "https://docs.python.org/3/library/venv.html", + "https://packaging.python.org/tutorials/managing-dependencies/", + "https://pipenv.readthedocs.io/en/latest/", + "https://virtualenvwrapper.readthedocs.io/en/latest/", + "naucse:page?lesson=beginners/cmdline", + "naucse:page?lesson=beginners/install" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/fast-track/install/index.md", + "title": "Vytvoření virtuáního prostředí", + "vars": {} + } + }, + "source_file": "lessons/fast-track/install/info.yml", + "static_files": {}, + "title": "Vytvoření virtuáního prostředí" + }, + "fast-track/yaml": { + "pages": { + "index": { + "attribution": [ + "Napsal Mikuláš Poul, 2018" + ], + "content": { + "path": "yaml/index.html" + }, + "ids": [ + "o_formatu_yaml" + ], + "license": "cc-by-sa-40", + "license_code": "cc0", + "links": [ + "#o_formatu_yaml" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/fast-track/yaml/index.md", + "title": "O formátu YAML", + "vars": {} + } + }, + "source_file": "lessons/fast-track/yaml/info.yml", + "static_files": {}, + "title": "O formátu YAML" + }, + "git/basics": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2015-2017." + ], + "content": { + "path": "basics/index.html" + }, + "css": ".green { color: #0a0; }\n.red { color: #a00; }\n.yellow { color: #a50; }\n.strong { font-weight: bold; }\n.blue { color: #0aa; }\n", + "ids": [ + "diagram", + "druha_revize", + "git", + "gitk", + "instalace", + "log", + "prvni_revize", + "repozitar", + "zaver" + ], + "license": "cc-by-sa-40", + "links": [ + "#diagram", + "#druha_revize", + "#git", + "#gitk", + "#instalace", + "#log", + "#prvni_revize", + "#repozitar", + "#zaver", + "naucse:page?lesson=beginners/cmdline", + "naucse:page?lesson=git/install", + "naucse:static?filename=diagram.svg", + "naucse:static?filename=dropbox.png", + "naucse:static?filename=gitk.png" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/git/basics/index.md", + "title": "Git", + "vars": {} + } + }, + "source_file": "lessons/git/basics/info.yml", + "static_files": { + "diagram.svg": { + "path": "basics/diagram.svg" + }, + "dropbox.png": { + "path": "basics/dropbox.png" + }, + "gitk.png": { + "path": "basics/gitk.png" + } + }, + "title": "Git" + }, + "git/branching": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2015-2017." + ], + "content": { + "path": "branching/index.html" + }, + "ids": [ + "slouceni", + "vetveni_v_gitu" + ], + "license": "cc-by-sa-40", + "links": [ + "#slouceni", + "#vetveni_v_gitu", + "naucse:static?filename=branch1.png", + "naucse:static?filename=branches.png", + "naucse:static?filename=merge.png" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/git/branching/index.md", + "title": "Větvení v Gitu", + "vars": {} + } + }, + "source_file": "lessons/git/branching/info.yml", + "static_files": { + "branch1.png": { + "path": "branching/branch1.png" + }, + "branches.png": { + "path": "branching/branches.png" + }, + "merge.png": { + "path": "branching/merge.png" + } + }, + "title": "Větvení v Gitu" + }, + "git/collaboration": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies CZ napsali Petr Viktorin a Oskar Hollman, 2015-2017." + ], + "content": { + "path": "collaboration/index.html" + }, + "ids": [ + "aktualizace_smallcodegit_pullcodesmall", + "github", + "hlaseni_chyb_smallemissuesemsmall", + "licence", + "naklonovani_repozitare_smallcodegit_clonecodesmall", + "open_source", + "posilani_zmen_smallcodegit_pushcodesmall", + "readme_informace_pro_ostatni", + "spoluprace", + "vytvoreni_vetve", + "zadost_o_zacleneni_smallempull_requestemsmall" + ], + "license": "cc-by-sa-40", + "links": [ + "#aktualizace_smallcodegit_pullcodesmall", + "#github", + "#hlaseni_chyb_smallemissuesemsmall", + "#licence", + "#naklonovani_repozitare_smallcodegit_clonecodesmall", + "#open_source", + "#posilani_zmen_smallcodegit_pushcodesmall", + "#readme_informace_pro_ostatni", + "#spoluprace", + "#vytvoreni_vetve", + "#zadost_o_zacleneni_smallempull_requestemsmall", + "http://choosealicense.com/", + "http://creativecommons.org/choose/", + "https://bitbucket.org", + "https://choosealicense.com/licenses/agpl-3.0/", + "https://choosealicense.com/licenses/cc-by-sa-4.0/", + "https://choosealicense.com/licenses/mit/", + "https://chromium.googlesource.com/chromium/src.git", + "https://cs.wikipedia.org/wiki/Markdown", + "https://cs.wikipedia.org/wiki/Svobodn%C3%BD_software", + "https://en.wikipedia.org/wiki/Richard_Stallman", + "https://github.com", + "https://github.com/", + "https://github.com/GNOME/gedit", + "https://github.com/Microsoft/vscode", + "https://github.com/aosp-mirror", + "https://github.com/atom/atom", + "https://github.com/django/django", + "https://github.com/jupyter/notebook", + "https://github.com/kennethreitz/requests", + "https://github.com/matplotlib/matplotlib", + "https://github.com/mitsuhiko/flask", + "https://github.com/mozilla/gecko-dev", + "https://github.com/naucse/prezencka", + "https://github.com/numpy/numpy", + "https://github.com/pytest-dev/pytest/", + "https://github.com/python/cpython", + "https://github.com/pyvec/naucse.python.cz", + "https://github.com/torvalds/linux", + "https://gitlab.com/", + "https://launchpad.net/", + "https://opensource.org/licenses", + "https://pagure.io/", + "https://www.gnu.org/gnu/manifesto.en.html", + "naucse:static?filename=gh-workflow-diagram.svg" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/git/collaboration/index.md", + "title": "Spolupráce a Open source", + "vars": {} + } + }, + "source_file": "lessons/git/collaboration/info.yml", + "static_files": { + "gh-workflow-diagram.svg": { + "path": "collaboration/gh-workflow-diagram.svg" + } + }, + "title": "Spolupráce a Open source" + }, + "git/git-collaboration-2in1": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies CZ napsali Petr Viktorin a Oskar Hollman, 2015-2017." + ], + "content": { + "path": "git-collaboration-2in1/index.html" + }, + "ids": [ + "aktualizace_codegit_pullcode", + "git_a_github", + "log_codegit_logcode", + "open-source_a_free_software", + "posilani_zmen_codegit_pushcode", + "prace_s_lokalnim_repozitarem", + "prvni_revize_codegit_commitcode", + "spoluprace_a_git", + "spoluprace_na_projektu", + "stav_repozitare_codegit_statuscode", + "zadost_o_zacleneni_pull_request" + ], + "license": "cc-by-sa-40", + "links": [ + "#aktualizace_codegit_pullcode", + "#git_a_github", + "#log_codegit_logcode", + "#open-source_a_free_software", + "#posilani_zmen_codegit_pushcode", + "#prace_s_lokalnim_repozitarem", + "#prvni_revize_codegit_commitcode", + "#spoluprace_a_git", + "#spoluprace_na_projektu", + "#stav_repozitare_codegit_statuscode", + "#zadost_o_zacleneni_pull_request", + "https://choosealicense.com", + "https://creativecommons.org", + "https://cs.wikipedia.org/wiki/Svobodn%C3%BD_software", + "https://en.wikipedia.org/wiki/Richard_Stallman", + "https://github.com", + "https://github.com/asgeirrr/prezencka", + "https://github.com/asgeirrr/prezencka/", + "https://opensource.org", + "https://www.root.cz/knihy/katedrala-a-trziste/", + "naucse:page?lesson=beginners/cmdline", + "naucse:static?filename=diagram.png", + "naucse:static?filename=gh-workflow-diagram.svg" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/git/git-collaboration-2in1/index.md", + "title": "Spolupráce a Git", + "vars": {} + } + }, + "source_file": "lessons/git/git-collaboration-2in1/info.yml", + "static_files": { + "diagram.png": { + "path": "git-collaboration-2in1/diagram.png" + }, + "gh-workflow-diagram.svg": { + "path": "git-collaboration-2in1/gh-workflow-diagram.svg" + } + }, + "title": "Spolupráce a Git" + }, + "git/ignoring": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies CZ napsal Petr Viktorin, 2017." + ], + "content": { + "path": "ignoring/index.html" + }, + "ids": [ + "automaticke_pridavani", + "dalsi_haraburdi", + "format_ignorovaciho_souboru", + "ignorovani_souboru", + "osobni_poznamky", + "priprava", + "vystupy_programu_a_pomocne_soubory_spolecnych_nastroju" + ], + "license": "cc-by-sa-40", + "links": [ + "#automaticke_pridavani", + "#dalsi_haraburdi", + "#format_ignorovaciho_souboru", + "#ignorovani_souboru", + "#osobni_poznamky", + "#priprava", + "#vystupy_programu_a_pomocne_soubory_spolecnych_nastroju", + "https://git-scm.com/docs/gitignore", + "https://inkscape.org/" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/git/ignoring/index.md", + "title": "Ignorování souborů", + "vars": {} + } + }, + "source_file": "lessons/git/ignoring/info.yml", + "static_files": {}, + "title": "Ignorování souborů" + }, + "git/install": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017" + ], + "content": { + "path": "install/index.2.html" + }, + "ids": [ + "config", + "git", + "linux", + "macos", + "nastaveni", + "windows" + ], + "license": "cc-by-sa-40", + "links": [ + "#config", + "#git", + "#linux", + "#macos", + "#nastaveni", + "#windows", + "https://git-scm.org" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/git/install/index.md", + "title": "Instalace Gitu", + "vars": {} + } + }, + "source_file": "lessons/git/install/info.yml", + "static_files": {}, + "title": "Instalace Gitu" + }, + "intro/async": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Petr Viktorin, Miro Hrončok a další, 2016-2017." + ], + "content": { + "path": "async/index.html" + }, + "ids": [ + "a_dalsi", + "alternativni_smycky_udalosti", + "async_funkce_a_task", + "asynchronni_cykly_a_kontexty", + "asyncio", + "event_loop", + "fan-out_a_fan-in", + "jeden_standard", + "komunikace", + "soubeznost_v_pythonu" + ], + "license": "cc-by-sa-40", + "links": [ + "#a_dalsi", + "#alternativni_smycky_udalosti", + "#async_funkce_a_task", + "#asynchronni_cykly_a_kontexty", + "#asyncio", + "#event_loop", + "#fan-out_a_fan-in", + "#jeden_standard", + "#komunikace", + "#soubeznost_v_pythonu", + "http://www.tornadoweb.org/en/stable/", + "https://creativecommons.org/licenses/by-nc/2.5/", + "https://docs.python.org/3/library/asyncio-dev.html#asyncio-dev", + "https://docs.python.org/3/library/asyncio-eventloop.html#executor", + "https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server-protocol", + "https://docs.python.org/3/library/asyncio-sync.html", + "https://docs.python.org/3/library/asyncio-task.html#asyncio.gather", + "https://greenlet.readthedocs.io/en/latest/", + "https://imgs.xkcd.com/comics/standards.png", + "https://pypi.org/project/asyncqt/", + "https://pypi.org/project/uvloop/", + "https://trio.readthedocs.io/en/stable/tutorial.html", + "https://twistedmatrix.com/trac/", + "https://www.python.org/dev/peps/pep-0249/", + "https://www.python.org/dev/peps/pep-3156/", + "https://www.python.org/dev/peps/pep-3333/", + "https://xkcd.com/927/", + "naucse:page?lesson=advanced/generators", + "naucse:page?lesson=intro/cython", + "naucse:page?lesson=intro/pyqt" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/async/index.md", + "title": "AsyncIO", + "vars": {} + } + }, + "source_file": "lessons/intro/async/info.yml", + "static_files": {}, + "title": "AsyncIO" + }, + "intro/click": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017." + ], + "content": { + "path": "click/index.html" + }, + "ids": [ + "a_dalsi", + "argumenty", + "argumenty_prikazove_radky", + "click", + "podprikazy", + "prikazy_a_prepinace", + "soubory", + "validace_vstupu" + ], + "license": "cc-by-sa-40", + "links": [ + "#a_dalsi", + "#argumenty", + "#argumenty_prikazove_radky", + "#click", + "#podprikazy", + "#prikazy_a_prepinace", + "#soubory", + "#validace_vstupu", + "https://click.palletsprojects.com/en/7.x/", + "https://click.palletsprojects.com/en/7.x/api/#click.BadParameter", + "https://click.palletsprojects.com/en/7.x/api/#click.File", + "https://click.palletsprojects.com/en/7.x/api/#click.Path", + "https://click.palletsprojects.com/en/7.x/api/#click.UsageError", + "https://click.palletsprojects.com/en/7.x/api/#types", + "https://click.palletsprojects.com/en/7.x/arguments/", + "https://click.palletsprojects.com/en/7.x/options/", + "https://click.palletsprojects.com/en/7.x/options/#callbacks-for-validation", + "https://docs.python.org/3/library/argparse.html", + "https://docs.python.org/3/library/functions.html#open", + "https://docs.python.org/3/library/getopt.html", + "https://docs.python.org/3/library/optparse.html", + "https://docs.python.org/3/library/sys.html#sys.argv" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/click/index.md", + "title": "Click – Rozhraní pro příkazovou řádku", + "vars": {} + } + }, + "source_file": "lessons/intro/click/info.yml", + "static_files": {}, + "title": "Click – Rozhraní pro příkazovou řádku" + }, + "intro/cython": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Petr Viktorin, Miro Hrončok a další, 2016-2017." + ], + "content": { + "path": "cython/index.html" + }, + "ids": [ + "anotace", + "c_api", + "cpython", + "cython", + "direktivy", + "doplneni_typu", + "externi_knihovny", + "gil", + "hodnoty_a_vyjimky", + "kompilace_pythonu", + "modul_v_c", + "pouziti_knihoven_z_c", + "pouzivani_numpy", + "preklad", + "pyobject", + "reference_counting", + "rychlost", + "struktury_ukazatele_a_dynamicka_alokace", + "tri_typy_funkci", + "tridy", + "video", + "zkratky_codepyximportcode_a_codecythoncode" + ], + "license": "cc-by-sa-40", + "links": [ + "#anotace", + "#c_api", + "#cpython", + "#cython", + "#direktivy", + "#doplneni_typu", + "#externi_knihovny", + "#gil", + "#hodnoty_a_vyjimky", + "#kompilace_pythonu", + "#modul_v_c", + "#pouziti_knihoven_z_c", + "#pouzivani_numpy", + "#preklad", + "#pyobject", + "#reference_counting", + "#rychlost", + "#struktury_ukazatele_a_dynamicka_alokace", + "#tri_typy_funkci", + "#tridy", + "#video", + "#zkratky_codepyximportcode_a_codecythoncode", + "http://cffi.readthedocs.io/en/latest/", + "http://cython.readthedocs.io/en/latest/src/tutorial/cdef_classes.html", + "http://valgrind.org/", + "https://cython.readthedocs.io/src/userguide/source_files_and_compilation.html#compiler-directives", + "https://cython.readthedocs.io/src/userguide/source_files_and_compilation.html#how-to-set-directives", + "https://docs.python.org/3/c-api/long.html#c.PyLong_FromLong", + "https://docs.python.org/3/c-api/module.html#c.PyModuleDef_Slot", + "https://docs.python.org/3/c-api/refcounting.html#c.Py_DECREF", + "https://docs.python.org/3/c-api/refcounting.html#c.Py_INCREF", + "https://docs.python.org/3/c-api/structures.html#c.PyObject", + "https://docs.python.org/3/distutils/apiref.html#distutils.core.Extension", + "https://docs.python.org/3/library/ctypes.html", + "https://en.wikipedia.org/wiki/GNU_Debugger", + "https://github.com/hroncok/", + "https://github.com/joerick/cibuildwheel#cibuildwheel", + "https://github.com/pypa/manylinux", + "https://github.com/python/cpython/blob/3.5/Include/floatobject.h#L15", + "https://github.com/python/cpython/blob/3.5/Include/listobject.h#L23", + "https://github.com/python/cpython/blob/3.5/Include/longintrepr.h#L89", + "https://github.com/python/cpython/blob/3.5/Include/object.h#L106", + "https://pypi.python.org/pypi/pytest-profiling", + "https://travis-ci.org/", + "https://wiki.python.org/moin/WindowsCompilers", + "https://www.appveyor.com/", + "https://www.youtube.com/watch?v=Ksv4RA6yhkY", + "naucse:static?filename=test_matmul.py" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/cython/index.md", + "title": "Cython", + "vars": {} + } + }, + "source_file": "lessons/intro/cython/info.yml", + "static_files": { + "test_matmul.py": { + "path": "cython/test_matmul.py" + } + }, + "title": "Cython" + }, + "intro/decorators": { + "pages": { + "index": { + "attribution": [ + "Lubomír Sedlář, Lumír Balhar 2019-2020." + ], + "content": { + "path": "decorators/index.html" + }, + "ids": [ + "dekoratory", + "napoveda_pro_funkce", + "predavani_vsech_argumentu", + "priklad_0_registrace_funkci", + "priklad_1_trasovani_volani_funkci", + "priklad_2_opakovani_http_pozadavku", + "solution-0", + "teorie_do_zacatku" + ], + "license": "cc-by-sa-40", + "links": [ + "#dekoratory", + "#napoveda_pro_funkce", + "#predavani_vsech_argumentu", + "#priklad_0_registrace_funkci", + "#priklad_1_trasovani_volani_funkci", + "#priklad_2_opakovani_http_pozadavku", + "#teorie_do_zacatku", + "naucse:solution?solution=0" + ], + "slug": "index", + "solutions": [ + { + "content": "
def co_se_deje(func):\n    def nahradni_funkce(x):\n        print(f"Voláme {func.__name__}({x})")\n        vysledek = func(x)\n        print(f"Výsledek {func.__name__}({x}) = {vysledek}")\n        return vysledek\n\n    return nahradni_funkce\n
" + } + ], + "source_file": "lessons/intro/decorators/index.md", + "title": "Dekorátory", + "vars": {} + } + }, + "source_file": "lessons/intro/decorators/info.yml", + "static_files": {}, + "title": "Dekorátory" + }, + "intro/deployment": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017." + ], + "content": { + "path": "deployment/index.html" + }, + "ids": [ + "deployment_webovych_aplikaci", + "wsgi" + ], + "license": "cc-by-sa-40", + "links": [ + "#deployment_webovych_aplikaci", + "#wsgi", + "http://gunicorn.org/", + "https://www.python.org/dev/peps/pep-0333/", + "https://www.pythonanywhere.com/", + "naucse:page?lesson=intro/deployment&page=pythonanywhere", + "naucse:page?lesson=intro/flask" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/deployment/index.md", + "title": "Deployment webových aplikací", + "vars": {} + }, + "pythonanywhere": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017." + ], + "content": { + "path": "deployment/pythonanywhere.html" + }, + "ids": [ + "aktualizace", + "deployment_soukromych_udaju", + "deployment_webovych_aplikaci_na_pythonanywhere" + ], + "license": "cc-by-sa-40", + "links": [ + "#aktualizace", + "#deployment_soukromych_udaju", + "#deployment_webovych_aplikaci_na_pythonanywhere", + "https://help.pythonanywhere.com/pages/environment-variables-for-web-apps/", + "https://www.pythonanywhere.com/", + "https://www.pythonanywhere.com/forums/topic/12878/#id_post_52160" + ], + "slug": "pythonanywhere", + "solutions": [], + "source_file": "lessons/intro/deployment/pythonanywhere.md", + "subtitle": "PythonAnywhere", + "title": "Deployment webových aplikací – PythonAnywhere", + "vars": {} + } + }, + "source_file": "lessons/intro/deployment/info.yml", + "static_files": {}, + "title": "Deployment webových aplikací" + }, + "intro/distribution": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017." + ], + "content": { + "path": "distribution/index.html" + }, + "ids": [ + "dalsi", + "datove_soubory", + "extra_soubory_do_zdrojoveho_balicku", + "instalace_pomoci_pip", + "moduly", + "nahrani_na_pypi", + "programy_pro_prikazovou_radku", + "setuppy", + "slovnicek_pojmu", + "soubor_requirementstxt", + "specifikace_zavislosti", + "spousteni_modulu", + "vice_argumentu_pro_setup", + "vice_souboru_s_python_kodem", + "wheel_binarni_balicky" + ], + "license": "cc-by-sa-40", + "license_code": "cc0", + "links": [ + "#dalsi", + "#datove_soubory", + "#extra_soubory_do_zdrojoveho_balicku", + "#instalace_pomoci_pip", + "#moduly", + "#nahrani_na_pypi", + "#programy_pro_prikazovou_radku", + "#setuppy", + "#slovnicek_pojmu", + "#soubor_requirementstxt", + "#specifikace_zavislosti", + "#spousteni_modulu", + "#vice_argumentu_pro_setup", + "#vice_souboru_s_python_kodem", + "#wheel_binarni_balicky", + "https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt", + "https://docs.python.org/3/distutils/sourcedist.html#specifying-the-files-to-distribute", + "https://gist.github.com/oskar456/e91ef3ff77476b0dbc4ac19875d0555e", + "https://packaging.python.org/", + "https://packaging.python.org/distributing/#package-data", + "https://packaging.python.org/distributing/#setup-args", + "https://packaging.python.org/glossary", + "https://pypi.org", + "https://pypi.org/account/register/", + "https://pypi.org/pypi?%3Aaction=list_classifiers", + "https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins", + "https://test.pypi.org/account/register/", + "https://wiki.python.org/moin/TestPyPI" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/distribution/index.md", + "title": "Moduly", + "vars": {} + } + }, + "source_file": "lessons/intro/distribution/info.yml", + "static_files": {}, + "title": "Moduly" + }, + "intro/docs": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017." + ], + "content": { + "path": "docs/index.html" + }, + "ids": [ + "_doctest", + "_testcleanup", + "_testcode", + "_testoutput", + "_testsetup", + "autodoc", + "co_do_dokumentace_psat", + "doctest", + "dokumentace", + "import_z_vlastniho_kodu", + "kompletni_priklad", + "nastaveni_a_rozsireni", + "odkazy_na_tridy_a_moduly", + "read_the_docs", + "readmerst", + "sphinx", + "textovy_obsah_v_dokumentaci", + "travis_ci" + ], + "license": "cc-by-sa-40", + "links": [ + "#_doctest", + "#_testcleanup", + "#_testcode", + "#_testoutput", + "#_testsetup", + "#autodoc", + "#co_do_dokumentace_psat", + "#doctest", + "#dokumentace", + "#import_z_vlastniho_kodu", + "#kompletni_priklad", + "#nastaveni_a_rozsireni", + "#odkazy_na_tridy_a_moduly", + "#read_the_docs", + "#readmerst", + "#sphinx", + "#textovy_obsah_v_dokumentaci", + "#travis_ci", + "http://www.sphinx-doc.org/", + "http://www.sphinx-doc.org/en/master/domains.html#cross-referencing-python-objects", + "http://www.sphinx-doc.org/en/master/ext/autodoc.html#directive-automodule", + "http://www.sphinx-doc.org/en/master/ext/doctest.html", + "http://www.sphinx-doc.org/en/master/ext/napoleon.html", + "http://www.sphinx-doc.org/en/master/markup/inline.html#role-ref", + "http://www.sphinx-doc.org/en/master/markup/misc.html#index-generating-markup", + "http://www.sphinx-doc.org/en/stable/rest.html", + "http://www.writethedocs.org/guide/writing/beginners-guide-to-docs/", + "https://github.com/ralsina/rst-cheatsheet", + "https://readthedocs.org/", + "https://recommonmark.readthedocs.io/en/latest/auto_structify.html#embed-restructuredtext", + "https://www.sphinx-doc.org/en/master/usage/markdown.html", + "naucse:page?lesson=intro/testing" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/docs/index.md", + "title": "Dokumentace", + "vars": {} + } + }, + "source_file": "lessons/intro/docs/info.yml", + "static_files": {}, + "title": "Dokumentace" + }, + "intro/flask": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017." + ], + "content": { + "path": "flask/index.html" + }, + "ids": [ + "a_dalsi", + "blueprint_moduly", + "create_app_factory", + "dynamicke_routy", + "escaping", + "filtry", + "flask", + "ladici_rezim", + "logovani", + "sablony", + "staticke_soubory", + "vetsi_flask_aplikace", + "vlastni_podtrida_flask", + "webove_aplikace_flask", + "ziskani_url" + ], + "license": "cc-by-sa-40", + "links": [ + "#a_dalsi", + "#blueprint_moduly", + "#create_app_factory", + "#dynamicke_routy", + "#escaping", + "#filtry", + "#flask", + "#ladici_rezim", + "#logovani", + "#sablony", + "#staticke_soubory", + "#vetsi_flask_aplikace", + "#vlastni_podtrida_flask", + "#webove_aplikace_flask", + "#ziskani_url", + "http://127.0.0.1:5000/hello/", + "http://www.pylonsproject.org/", + "https://cs.wikipedia.org/wiki/Diazotypie", + "https://cs.wikipedia.org/wiki/Model-view-controller", + "https://developer.mozilla.org/en-US/docs/Web", + "https://developer.mozilla.org/en-US/docs/Web/CSS", + "https://developer.mozilla.org/en-US/docs/Web/HTML", + "https://docs.python.org/3/library/logging.html#module-logging", + "https://flask-pymongo.readthedocs.io/en/latest/", + "https://flask-sqlalchemy.palletsprojects.com/en/2.x/", + "https://flask.palletsprojects.com", + "https://flask.palletsprojects.com/en/1.1.x/api/#flask.Blueprint", + "https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.register_blueprint", + "https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.route", + "https://flask.palletsprojects.com/en/1.1.x/api/#flask.url_for", + "https://flask.palletsprojects.com/en/1.1.x/blueprints/", + "https://flask.palletsprojects.com/en/1.1.x/cli/", + "https://flask.palletsprojects.com/en/1.1.x/extensions/?highlight=extensions", + "https://flask.palletsprojects.com/en/1.1.x/logging/", + "https://flask.palletsprojects.com/en/1.1.x/patterns/appfactories/", + "https://flask.palletsprojects.com/en/1.1.x/quickstart/", + "https://flask.palletsprojects.com/en/1.1.x/quickstart/#variable-rules", + "https://jinja.palletsprojects.com/en/2.10.x/api/#jinja2.Markup", + "https://jinja.palletsprojects.com/en/2.10.x/templates/", + "https://jinja.palletsprojects.com/en/2.10.x/templates/#builtin-filters", + "https://jinja.palletsprojects.com/en/2.10.x/templates/#for", + "https://www.djangoproject.com/", + "naucse:page?lesson=fast-track/http#url-anatomy" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/flask/index.md", + "title": "Flask", + "vars": {} + } + }, + "source_file": "lessons/intro/flask/info.yml", + "static_files": {}, + "title": "Flask" + }, + "intro/json": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2015-2017." + ], + "content": { + "path": "json/index.html" + }, + "ids": [ + "hezky_vystup", + "jina_kodovani", + "json", + "kodovani", + "kodovani_dat", + "toml", + "typy", + "yaml" + ], + "license": "cc-by-sa-40", + "links": [ + "#hezky_vystup", + "#jina_kodovani", + "#json", + "#kodovani", + "#kodovani_dat", + "#toml", + "#typy", + "#yaml", + "http://en.wikipedia.org/wiki/Bencode", + "http://json.org/", + "http://www.yaml.org/", + "https://docs.python.org/3/library/json.html", + "https://docs.python.org/3/library/pickle.html", + "https://github.com/toml-lang/toml", + "https://yaml.org/" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/json/index.md", + "title": "JSON", + "vars": {} + } + }, + "source_file": "lessons/intro/json/info.yml", + "static_files": {}, + "title": "JSON" + }, + "intro/magic": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Petr Viktorin, Miro Hrončok a další, 2016-2017." + ], + "content": { + "path": "magic/index.html" + }, + "ids": [ + "a_dalsi", + "dekoratory", + "deskriptory", + "konstruktor", + "magie", + "metatridy", + "specialni_metody", + "ukol", + "velka_moc_a_velka_zodpovednost" + ], + "license": "cc-by-sa-40", + "links": [ + "#a_dalsi", + "#dekoratory", + "#deskriptory", + "#konstruktor", + "#magie", + "#metatridy", + "#specialni_metody", + "#ukol", + "#velka_moc_a_velka_zodpovednost", + "http://docs.pylonsproject.org/projects/pyramid/en/latest/_modules/pyramid/decorator.html", + "https://docs.python.org/3/library/pathlib.html", + "https://docs.python.org/3/reference/datamodel.html#special-method-names", + "https://www.python.org/download/releases/2.3/mro/", + "https://www.youtube.com/watch?v=NiSqG6s8skA" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/magic/index.md", + "title": "Magie", + "vars": {} + } + }, + "source_file": "lessons/intro/magic/info.yml", + "static_files": {}, + "title": "Magie" + }, + "intro/micropython": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Petr Viktorin, Miro Hrončok a další, 2016-2017." + ], + "content": { + "path": "micropython/index.1.html" + }, + "ids": [ + "blikani", + "drivery_a_pripojeni", + "flashovani", + "komunikace", + "led_pasek_ws2812", + "micropython", + "micropython_na_esp8266nodemcu", + "ovladani_konzole", + "popis_desky", + "souborovy_system", + "vstup_a_vystup", + "webrepl" + ], + "license": "cc-by-sa-40", + "links": [ + "#blikani", + "#drivery_a_pripojeni", + "#flashovani", + "#komunikace", + "#led_pasek_ws2812", + "#micropython", + "#micropython_na_esp8266nodemcu", + "#ovladani_konzole", + "#popis_desky", + "#souborovy_system", + "#vstup_a_vystup", + "#webrepl", + "http://api.thingspeak.com/channels/1417/field/2/last.txt", + "http://docs.micropython.org/en/latest/esp8266/", + "http://docs.micropython.org/en/latest/esp8266/esp8266/quickref.html#onewire-driver", + "http://docs.micropython.org/en/latest/esp8266/esp8266/quickref.html#software-spi-bus", + "http://docs.micropython.org/en/latest/esp8266/library/machine.I2C.html#machine-i2c", + "http://micropython.org/download#esp8266", + "http://micropython.org/webrepl/", + "http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html", + "https://github.com/espressif/esptool#usage", + "https://github.com/stlk/micropython/tree/master/workshop", + "https://naucse.python.cz/2019/brno-podzim-pondeli/sessions/micropython/", + "https://pyvec.github.io/cheatsheets/micropython/nodemcu-cs.pdf", + "https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/micropython/index.md", + "title": "MicroPython", + "vars": {} + } + }, + "source_file": "lessons/intro/micropython/info.yml", + "static_files": {}, + "title": "MicroPython" + }, + "intro/mypy": { + "pages": { + "index": { + "attribution": [ + "Pro kurz pokročilého Pythonu PyLadies Ostrava napsal Lumír Balhar, 2020." + ], + "content": { + "path": "mypy/index.html" + }, + "ids": [ + "a_mnohem_vice", + "alternativni_zpusoby_definice", + "definice_typu_a_jejich_kontrola", + "globalni_promenne", + "instalace_a_spusteni", + "mypy", + "slozitejsi_definice_a_modul_codetypingcode", + "staticka_a_dynamicka_typova_kontrola", + "vlastni_datove_typy" + ], + "license": "cc-by-sa-40", + "license_code": "cc0", + "links": [ + "#a_mnohem_vice", + "#alternativni_zpusoby_definice", + "#definice_typu_a_jejich_kontrola", + "#globalni_promenne", + "#instalace_a_spusteni", + "#mypy", + "#slozitejsi_definice_a_modul_codetypingcode", + "#staticka_a_dynamicka_typova_kontrola", + "#vlastni_datove_typy", + "https://docs.python.org/3/library/typing.html", + "https://mypy.readthedocs.io/en/stable/index.html", + "https://pypi.org/project/pytest-mypy/", + "https://www.python.org/dev/peps/pep-0585/" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/mypy/index.md", + "title": "Mypy — statická typová kontrola pro Python", + "vars": {} + } + }, + "source_file": "lessons/intro/mypy/info.yml", + "static_files": {}, + "title": "Mypy — statická typová kontrola pro Python" + }, + "intro/notebook": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Petr Viktorin, Miro Hrončok a další, 2016-2017." + ], + "content": { + "path": "notebook/index.html" + }, + "ids": [ + "jak_na_notebook", + "jupyter_notebook" + ], + "license": "cc-by-sa-40", + "license_code": "cc0", + "links": [ + "#jak_na_notebook", + "#jupyter_notebook", + "https://github.com/ipython/ipython/wiki/IPython-kernels-for-other-languages" + ], + "modules": { + "katex": "0.7.1" + }, + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/notebook/index.ipynb", + "title": "Notebook", + "vars": {} + } + }, + "source_file": "lessons/intro/notebook/info.yml", + "static_files": {}, + "title": "Notebook" + }, + "intro/numpy": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Petr Viktorin, Miro Hrončok a další, 2016-2017." + ], + "content": { + "path": "numpy/index.html" + }, + "ids": [ + "booleovske_hodnoty_poli", + "broadcasting_a_zmeny", + "dalsi_operace", + "datove_typy", + "indexovani", + "matematika_a_grafy", + "maticove_nasobeni", + "nejednoznacnost_a_zkratky", + "numpy", + "obrazky", + "priklady_pouziti", + "reshape", + "tvoreni_matic_cast_2", + "zakladni_operace", + "zvuk" + ], + "license": "cc-by-sa-40", + "links": [ + "#booleovske_hodnoty_poli", + "#broadcasting_a_zmeny", + "#dalsi_operace", + "#datove_typy", + "#indexovani", + "#matematika_a_grafy", + "#maticove_nasobeni", + "#nejednoznacnost_a_zkratky", + "#numpy", + "#obrazky", + "#priklady_pouziti", + "#reshape", + "#tvoreni_matic_cast_2", + "#zakladni_operace", + "#zvuk", + "data:audio/wav;base64,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", + "data:image/png;base64,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\n", + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASoAAAD8CAYAAADAKumpAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nOy9a5Alx3Xn98usrLp1697u6enpGQwGgwcBiKRALJ8g9bBEvajlPiV5Y722/JLCG8FQrPzNDlt+hMPhkO31WrHWah2SQ7she7UvaZdyUNwlbUmUSEGQRBIUSELA4ElgCAwGMz09Pd23b9etysrK9IesU1V3KJOAllxhQ5MRwMx031uPzJPn8T//c1KFELg1bo1b49Z4Iw/9J/0At8atcWvcGl9r3FJUt8atcWu84cctRXVr3Bq3xht+3FJUt8atcWu84cctRXVr3Bq3xht+3FJUt8atcWu84cc3RFEppf6cUuoZpdTzSqmf+Ebc49a4NW6NPz1Dfb15VEqpBHgW+H7gEvAo8MMhhAtf1xvdGrfGrfGnZnwjPKr3Ac+HEF4IIVjgl4Af/Abc59a4NW6NPyXDfAOueQfw8ujfl4BvuflDSqkPAR8CmEwm77njjnMopQAFRC8vhABKoZUihND9nuF38ToQQGmFF+8wfq3/vPiMwXu0TgjBxw+EIH/09w7Bo0b3u9nfVP3/Az4EtNIoBT6E/ncheJTWBO+7L6i1Z0feJcRLyf26F4IQ8N6jtSYEud3wvvLZ0D/P+pz084Ii4FFrn2L07mrtmmvPAYRuKdTomvI5ed/Q3VfJddfmaVjD4OOcyM+UUt33A4rxfftvMpYFWYuveMcQPxtCIECcM++HuRkW+OZZIBAIIX5HAb5br/6To0f5WnMT76cIhCiv8UXXnn38SvGxwrCWIXTrHefDd3I4Xq/+nUfzIGuhlY5yTZRFrfVXyIzcXnW/C6N5CaN/y/Otz9V4TYfnj9de/+z4d7In5R5jGdrdvcZisbh5Wf7I8Y1QVK9phBB+Hvh5gPvvvy/8rz/1v9A6TwgalMMYg3OOJNFx0okC1bZxA3vvMcbgvSe0ntDJrFcwm+S07XCNABhj0Fp31zS0rUMpjdI+CqsyeA9JwpqScMGjFP13p9lw7aZxaK1JkvhcSkGSxHv2+8NolJINGp/TBI1JNHhw3pGmWacgNU1jUSq+c1VVpGlcovEze+/je4f45/g5xs+eZVn/rG3r8N6TJIY0jc/oA/08hgAET5pmOOfi86bdM9aWLDX955zr5rZ7Hrm2rJGsU2oMTWP75zGpoa4tSRL/7YPrr+m9RzGstcyH9562dUwmOXVd4ZwjyzIAfOtJ0/j9aHzis8gzpqnp18Ukpr9mCB5tNE3jmEzi+6rAV6yT975fi9D6fm1D8IRUowIkqvszGeS1bePaZFnWz0uAfq6U0jS26p9FZFrmSeRb5kKGtZaiKIZnCJAkup8noDOarF2X4NfnWQ33GL9z20Z5Gs9pXVsmk6yTN/p7yL2ttSjM2v2U9r2MyHs1zTA3dHL4n/3n/8Vr1hffiNDvFeDO0b/Pdz/7qqNtHcfHZa+YZMLi4sbNON4M8aXjBhXtnyR6pOCikCaJ6T4fN7wsapJEJVc7iwca7yDR/UaVe8n9tNb9xIcQhWYyyfqfJYnurh+/IwLRNA5rbdwc3TWyLOvfJwRoGkvbxsWNm9x1mzPrrwWsCTFAmkYBmkyyfvMbE+fMGNM/T9NYgF6hxY3EsAlHFlHePU1N/4xyrfFmGCuS8bXiWvpescvvQvCUxyWJ1vjWR28TqKqq8347BRHiPLStp6qqfiPGv5tO+fpeZkSRee+p6wqtNWmnVKMSt3gfr2nt8PcoG7pTnKbfpHVd9bKRdMrNe4+1ljSNSl7eM0nM2tzI70SG5Z3GhkUp3ckDvZxmWYZOTPQIE01iTKcsfD83StErvmgIB3lcrcr+XiI/3kdFKUpF5FHkU74rzy9yLvNrrcU5R57nvUzEtWAkV1EZJYnuDWCWZf11REHJd8WA98ZCvXb1841QVI8C36SUepNSKgP+PeCjX+0L0e2E2azAWrumEEKIAiCKwPsowHVt+8kX7R7CILxVVRGC7zdp/Pug2ESQTZbhgSQ1+O7z02nRb8qxcIhwyWJVVUXbun4hZUOBLMagVOXf1trOa4rXkOcWQZNNKErAGEOaZr1SHluq6DVka0pCrG3bun6+5PeTSdb/Tv6TOZTPAxRFAcB0mnfCZtesdnxus/aduCnWPaLo3fj+vfIsQxO9kERpJpO8F+bQeSXiSYhwi0LJ87x/HzFgeZ73m00MwNjyj68pcwZRwctc5XmOUjCZxI01m83XvHUxUlmWUdeWura9FyKKcCyvN6+PrIcoqOhRxfUWZZQkOnruxtASjWbbDgZiUIxxg8uzihKczeb9OohMTKdF/7Oxt9a20WuWORfjGA0cnezF9UwS0xvRuBcHr3NddscRj+u82EGuxUMXp6P32F9HIu/rrqhCCA74T4FfA54C/lkI4cmv+T1AJZCk0QOIVk9eiPWFDmC0Jk0M9arCEy2AVmDQ/eYQQWudo1pVuMbSOkfwnrqqSI1BO02KZqINBtasqdYa1XoybUgCZDqGbInWZGlGagygCUGjVPx7VBz0wk/rydP4J61nmmYoDW3wYMC1kE0ytNGYTJNNsohvEbEE2zha73Fth3d1IW7bYT4x3HPdRjUD5jJSLHVddd4EOOdxLgqSgmjVQgyNxEMoyzIKU+Nj2Jdl1NbSeo9tLDox8U/lUUTvyDuP7vAXk3Tzk2UkJnoKaZbhFTTek2SGoME7T5pkZCbDaINvHakxpEYzyUyvyCSsco0l6TAo27+T75VxCJBmBh8saQZGgQ6eVA/et+A4eMhMRlNb8IOnKjIWPCg0iTbxT2NAde8RPFnI0K1GeYNrYtipE4MPGtuIstRobTAmo3EWk2iCc6SdzI+NUqY1BkiVJukMtCi9+Nn4/Nbafr+IjPToXqcwGmtxjY1YnfeoXOOI8pNo03tLokSbxvaKq2ksvgWCJp8U8e+ZweHxnfEZe4tt63CtjRAKHh8cWtMbP+89aWJoG4d3jjQZvO6vAA2/yviG8KhCCB8PIbw5hHBfCOF//Jqf7/4UYRMLKZtNQicJ38TS1rUdhV6m9w7E+4EhlJEhgi2uaNs66tr27nHT2EGYGZSmKK7hOX1vvWXRgB4jEe/KGNMLl/weomclXqG10UrLcwumJIIn7rU8u8yBvK9gXG3r+3AxrkP8/NbW1oDrjDyStY3L8B7je0kocPN3hrUwvbczxlTEUst89ZigkmfI+ueXIZ8fbxrZSKtVueblTKdF7xk1jRg1Tes8WnehHKCNwfl1j0DmYFiTwajFuR4U/hBm+tF3s36+4jzHdbW2Ik01sP5ebet6eZPnkDUdr/V4vsQLGuTVUNdVjztKmDeec8GtxLsVzDTOI/1/YwhC1mmMe8l6VVVFnuckSpNqQxCl00ZjF430YBjFm5JngQEnlfkah5B/oh7VH2copUgS3QOvY/dwMsl7xSWKQTaLgH5jYFkWOcuyXtnJfMjCAINl6oRGRpKY3qrK/UwHCg+40gBCimIUBTKZ5P21xJ0eu8tyzY2NeY95iDDJ5m2aKLgx9h/eR64vP4t4TNaHlvKZcbiZJIajo2X33WxNmOSz4pmMQwPZjIIFmg43kXcZ3PkhvIwKd8Cy5D4iyGOlaK3tw3lZz7EnOgDfA3Yi/xblHvGpQbGCxnsIEa8lmxSgDKioDGR+ZEOKkRNwWRSuvN8YCxUPRxTD2GCkqUEnEV+yjaX1rleeUclW/RwJjDEGyyW0F+Bfnk/+E6xM5ns8xpjlGEuV+4nhjGD6AAnE+5rOyNGvhYTP4ySJDkRv2UOqDb4F39J7nXFv2F7hi7cm+KgkF0IYcNWx8/BaxhtCURFCBwrn/eYeA6LjbJ9kL9I064FTCVmMMT0YqhS9MMsGGAv1WNMDlGVJCL7HyESpDRhG3oOGYwykbX20Op0wjd11ETJRmmP8RvAyuZbE90B3vfjOq1VFXVe9lzFWsLKJRMAFL5ENLZtfsnzW2jVBluvJv8ehwHhz5nk+hIMMyr1pxhjM8PObraoA2qI4oyIePBkBl2PW0/U4UNNYqipu8jzPh7AsDEZArg0xrEmSjBA0JsloWo/JclS3BnVd9fiTyIcM59zoOVlL6sj6iBKR7N465uKxtkJr0FoUWNYrwLHsyrqL4RNwXORj/Kds+LEilc0e5+MrPauyLPt3Es9NEggRn7K9DEpUMeBKHcWh8xyVilAHHpT3NNaitXidEfYQgyF7VfaFvNNgNIdEwutwpuJcvL6Pf+OGDhCcw66qPr4db5gxOKuNBg3aaJwX8Nj3qetEQZoYQusxXVi45iITgXCjDQSPbx0m0bjGkRrd/0zhWa1K6noAzWWRhzSvZNW6rI+KfC2TRDxrDFR77zFJBkF3IIPmuK1oNTgPBNNvBsG6NjfmPS4zyTLySYaC7vpmzdWPSlX32SfxPiQsHkDiiCM4DZiYRBgrrz4MSkwPhhTTgiQxvefbto58Oo8UhxDxRQmnZrMC52z3+cFrGStiWQcfIu5jUkPrIHiNazxaRYU7APqONDFkJqNtHG3jQGnSLMO1nnxaENoKWstEgfGQ4KCt8E1FaD1t46I8KE2joAke36fcB6MhClWe1xiDArI06/BJ0+Mxgs0465mkOSpo8OvAeZoaWgXWO5KJQcyjeC9t60BDG1ykReAxRnde4ICZxu0qxo5R8mDIAofgMalhkufoRONaj2kNqdIU05zEsAYDiLcsctw0EUsj+B7jqlcxMeWCp24drasgxD8VcV28c+Td3suMAe/xzkXq0E24qSjzfyNDP/GaBg7HkG4VyyOhjizM2BsZK6KbM1JiMcZxv2Q8ZBKrqlqzoHVte09F8KPx72FIO4tnIx4HRGsZ7+37LOQ4ZBGvaz4tMFozMQYzwobEg6nraPWzLOtBUFlwuV9Zlv095J7jbJVkccbZn3HoIWD1+N9jT2KM50gWcoxRiScKg2cilltwlSzLek9gDGwLhldVUfi18lEReIcymtpZWjxexeRDSMBrT6s8vvGkOmNiMuzK4kUBGUOJRyXRkJnM4LwjSQ3pJKMNw7pG+dM9TghDKl88YsFAJXyRz0h4KrI20DFi+CrZ5bZ1JEpjtKYqKzT0xqSfz46PFQFnMwrX6OdI5lYoHeIZynqJR9grPwbjId6qrJl4T0LnEe8r7guL946mqQgd102U5TiEHGcetda9xyq0ILmXyJd49OI9q5vj2K8y3hCKKnShnwjHOGUqQyZ3yPIMsbxs2CyL8fZqVY6U3hALS9ggoZ+EdZLSHfOw5Ltj8C+CoqCU7wDlgfQmiyaubnwvvuIa8vs+ZQ7QetqOdNiTL1knY44VidxHQq6iKDDGMJ/P+3uMQ+cx72as5CWsEIytbQfejPw+erauF8zVquo9pHXvT8DbIS0N9Lw04eaM7z0OcbIsw2nwRuO0R00M03nOuTvPMSkytk9vM5nn6Ilm6/Q2ZmrITuZkJ3PyUwXbd+6wsz3n1Mk5QVvSqWZlS5LMYFtLXhR4PGVV4vE9AVHWdTLJ+6TJODEj8yfKStZFFIS8vxi8sSEYG0NnLUYbppM8eoStX5PHRMUs9ryYgx8nVOLfBQ4QuZX7yFrIXplOiz5MFsWkVMz8xj0wGE3ZN7IO1tp+T0R6wpD8GFMNxJuT9R0bQdkLYzqN0FvGuG7877Urqj8xZvrNY7wpxauScTPYKtkGwTpEyMTKTSZ5D8zL98XLElC1aSxBmz52jkIzeExjioJYDK01rq3WOCeDWz5YNsEmmmZgxctzCFAuSoTgSTpv0jeOthOOqHQH11zmRSyrKG5RrCIkYr28F9aywZg4V+LpyHW8ivQE3w4Cu1qVMusjgp7vMZ3JJOvIj9lI8Yh3YTqBHKyqYGDj9xh7p3meMZ/PaRrHly89z8WLF3n++ee5dm0X0ynVGzcOYvjkPYeHC3Z2djg6WvDgbWfY2NhEa81yuWRre4tV63j1+i7bp3e4/fZ7ee97H6KYF8yKOW2HLznvSYxBK41Rkb4i+J4MwTnHyYZ1D9qj1OCVTqd5Px9RcYjnH5VZnuU4a0kTQ5oYGjXmixmUjwTIelWSphm1cz2fb4zLyRq3o4SFtZG20WNaWlMURY9Vxecr+mTH2JuV9ZMw0lpLwBNwNE4SHobJJOsJnoIvDs8vxjkq0tWq6qsColEe7+/he69nvCEUlVKqB96Uii6k4ALBezQxw5AkGq007QhTaVtP0zGSTWLwgEo0usN7BPQVqyAeg3MOkxtSY1gul8xmxSjbQWdxI7ZA60kCGG0I3lBXQwaHzlVOEgPB0TiHbQawVQGtgJpa49TgbYxZy6ZTzmnHls7zPOJoXQhljOm5U1mXkZsYQ2Ntr6jT1NAGH8Mb4aB1qeTo6UTLLyFiaDwqjRtWJx13S7EmgPKeAiBLIkNY4CKMxhh8iByeWJoUSYxpZkiUhqCpnWM2K9g+vcmFC0/w6Ocf5/NffIzmuCRtPJV17O8foJXh+LhE6xiCS7axbi3FxjZpVvKX3/Egx4tdQrnLjWVJNp1j9xY0K8vtacZWbbny6MN88ksXqBrYs0umacHZc/fw4Ld+K9/81gepq5KDa3sYrSPx18fslmui94OCxjnMKDQeOwGSrQVoVcSV8BAax2qU2BAFkqQZVae4BXAWzygkBq9ApRl1Gz38mBzqlJ/WHZdOR0yxu3eSGFKT98osGiODrR2K+C5JYlh1CnCgCJjesI+fQylo0aiOR2VXFWYyJv26NeMMQ0QDdCU3sdxpOi06AzXOeoPOM3w9JDJey3hDKCopyBQrlaaS7TPdxEiYMcS3Es5kWRZBu27Cx1wNcW9jqDcOewYPyVrLdJqvAX7jzRk3b5zourZ4Bg6QeH0ikDB4QhCfOdG68zTovanomQx4iLjREOvXFHC8XPaej1xT3lmUUpJoWn1THZUesm0wZJgiZlT2zxuHXM8SQiQsyjNprfsSh96D6jblOIOVZWYUGmm0HvGjTM5xWXJqc452jt/69Q/zxBNPEFrLlStXaI7gqHE45zFBsyoXZFkOOILXmNkmtV+Sb2xGnlJjSZKMzTzHlwt8ZcFatvM56Iwb+/tsbmzRtp79V65w/vYtrh/usTxYcvLkHJYl157Y43cuXuDXladp4T3vez8PvOMhTs03OTw8QHlNkeRURO87NUMp0iCvjDyG+HvXejJjOD5eMs8Lgne9MoifWecViVcs8w3iAQ9UAuHhhRCpD/Iczjm0Gtjy8t0ITQx0F/FkJWMoWT/BFCXqkL0ie8ikGa0blVs169CGPMcYxxQumbzzmBkv3pTczzcxefV6En9vCEUVPar495vj27gphGTYYVQ9huN6UHJQADnWDYW9EBdRLLMor9msoKqq3huBdRBQUrCOLqsy8viEAyQp2HHhtO8xqS4Nn0ZlK8INrCkmETbhKTVVZNhPOzpEwHbvup4MECvmve890DSNDHchEGZZZF4bM2SJtNaUZbkWLo/Zz0MoHLOrkmRIknW8bDot+hKiqEQ9zg0p9ckk48TM8OWnn+Xjv/cwmdYUzYK7W0umDZut5pnjA1ZHFZPNbarakyQZronzsrm5FVn51tNax9HRkvnWJsujkuMq53BRYm3FRqKpliVp5pmkUVkl2rCxscn1q1dovOfusztcfOkim0XBPM1orSNRHo/hmU8/zBceeYRqlvG2B9/O933wL2DR1EtLNskie14Ry1vaoWYvenmD0TSZ6YxeEdn5fVmNH8K1tRq8AcjPMjNABcHftCZDUbMYK+89SoO1rscVQ4gQiJCjo7GQZIVbw1/lOUQGxPBEQzrc31nHfD6PCY2+AH1QaN575vN5zxMTBSths8AzwosTuW8aR9Jlr1/reEMoqhBCzyuadJ0PpNZJsl1jzGrMTNc6ZlNiXDwQOsfYkvBwxJJNp0VfjCpafpyBWS6XHB0tY82bGYDxtssciQUZLNYoIzTCNYRTJDwW74dWJ4IXRWa37jN9hRmASRF4ub+EC2MOmGRCxboJMDyQOKNnaszQkWE+n/dzJwmKtnX9swmHSauBCGht3Lgi5EdHi57F7b3tPcZTp7Z58skLfOQjH+HMlmNnc86s3kN7x6Wrl7nj9FlCVfGB7/p2Np58lsdefIndlWXVeLZncxaLJZsbc8rjimKq2ZxmhKZiPjEEW6KC5vK1BfZwyR3ncmYb80jvcJayrgga6qaiXWpaZ8mM4dq1PfKNguWyRHfPefcdd/Hq5SvYpuLMbJMpOa/8zsP81G98grCxyfu+5wN813e+H1YW33rQA/F1bET7jgGKWFjshaw5VFWIFyNJgzFQLwXRkowATZ5HPCjP835fLI+jPIpsKAYjJ1DB8fFyoH4EwQ4HnHYA+/3IAA3vIhy6gI6Qh48wQZJFzpRELD1dpPP4lYqQikQZ4m0KHjxwt7pEhFAXXoeOeEMoKqUUWR61bRsc2gzKYFXHdO64cjuGNp7pNI8lI37gEol34cNATnQu8pwklVzXEhrFCZduBqIUldLMZp3r2vh+s3oi94bgmRUF5bJE3VTaIq1GBEObdliTLKR0DogcpaF4GqJFDCq2qmkEfHZSr2hJsqwLhz1Ba1oNYeT6Hx8vmc3n2I4wGXG9gYA5zuppDVmWdx5ZB5I3A5tY3lmsY9PEmi6TZqzKCmMytM+pbYVOYHtni88++ggf++jnUKslxeol7jh1FxcvXCBNDadO7XD75g4maPaPD3jqhafJc8c9Z86w/+wlTs7nlEcHTCcZbVORaEPdOnRiqCpHPilwbQSjjxs4shWHr2jCPRlvufMcL7/wLCdP7dCUFa6qsFWFCzbyeBqHSQ1ZklGuKsqy5LnqaU5sbDHVGm1Lrts9zp3Y5t6zZ/jYY4/zyY9VPPKbn+At3/xWfuRHfpSD3UUkv2Y5oct8jUnI4jUHICTRyIjhDcH3fCJPVAp1t9EFFx0XwzvnmKUZq9qijaFxjiKP2UC87hILdtTpIfKSxMtzNvLFgo+tdOgUQ9tlAZvGMkkzVIgYcNtE+kfMPkZOlcAM6CEb6P0Qio65ekJtELkRIzbew6rjFypF5EB2yva1jjeMooLBE9Ha9BOlVCROiqeUJBH4rOuq19BN5x1Jqh7oPTJRGhIKpmm+xkkSCydZG8nYweBqh7DeK0gRPbjJJMOOUvmTSd5ZCdNbW+mUIOGi1ObJ+whQLCnjwBD3S3paLLFgZwLY50WO6Qp3h9Sy8Hq6Mp8R01iEp22HMEG8gTHbv2k6QLSKTGvB/Ewan3M6zalrxyqtmM4MN65d4sO/9PeZuAXuaEEWPOnxPu54E1YVd9x2L/vX96lWS06cmGO0o1rtkZiMq3sH1N6hmsiXovVo7UkTaBvLZDKQYJ3yJBoqW5Flmi9fXXDt2oI/MM+ys7XFlj1glmg2jSGdwO1nz3N9b5c8MywXC6qqorJVT+O4enWPIs8xSrNxpiCbQGNLJi1ce3GPJNccHi648MwTvOuhh/gP/t3/kINrBzgHk9x0DHr6cEvWKs5RDI3FCGqGBAVEDyR6TFEOi9k8UkE6vLXqKB3eW1JjemJzLEz3vXxLpCEGtixLpp1MLJfLkdxKttb0npis4xh/MyYC71IaFPen7z2i+O8IjYhXKBiqRC0SVoriMsmQXZSseKwEee0+1RtCUYUQetdVtPU4FVqvYpaq7xU1ypyIJwRSImN6JbBGAUgGZSNKId47/in8EMEJ4neHUoqela0gBNd7GToZClW996w64RSBXAsN9FByMSYcjpVYor8SLxJiYWSFxxBN0sWJ1n1WL0k0rff9nEgoIUCuYAwweE0DvyoCwmLh5RnHdI22HcihzjnINf/oH/4Cm9rhr19ia3uTpy9d5C333Ys1hlW9YGOrYHfvCocHJSe2Cl7d3eP0mW3yPOfi1QO+tLvHsjFsZwXJbN7xfGL4YdIBL0uLSOrUQJ4YVPDoBIoTW1RlxcuHJc/tHrA5ybHLJVppJpOXKPKM285s41rP/ffcT3a4z1Q5bhwuyFIJs6GtHTeqBabY5ur1JasQOyQcXSqpjuY8fO0TfP73H+ODf/7P8d3f9b3YZdkr+IgDRbxT5k7WTwxi2smPyEP07KOxTRLDUbkcaDQukl7zPKcuS0LjIBlksSzLPlSUcCuMPDiIiZ+iKHr4Q5Iy8vnJZADix6VV4xKyIQMcO2+MeXnCO5Q9JM+jlO6dhX7/MXAeZa9FQuy/YTwqpVRXpuJ7b0fIm6Kx5YXTNFbDw9BPiD421j2xTbhSxhhUiF6C1NwNBDQYsIEh6yf1Zkmie8sI3WYlVryaxKA0fasKsRYS4o0Lf+Udjo/LPvSSe/tmveJ9vKACUgvQHi10jtIar8CYiM/JvVarkoDu+3qFELOOEv5JXZ7M8ZjnBJCYoT6yaRxZZxjGlAXnPPPZnE9+8tf5nc99ivbogO95+wPsXrtCO8vYmM3Z3ztgNpuzshWHhwvmsx1aPGVTcubcOU6c2OYPH7/As9eXtNNNfGtpViWVhqKYQxdyRAZ2fJ/DwwM25gWr0pJPM47LAyap5vBglyzLWVWWVhmu15BPd7DWs/KevcOSPbvH0XHJUy9e5vxmxtvuOstsNufkiS1W5YJyecCZ/AwvvXqFRz/9NHayTW1LVoclmc44thoKQ3Ps+Pi//ChfvPAYH/r3f4zZbM7x8bKTy2yN0iHyOq7vk3US+ciygfhb2ai48jzvgflExcyrV2Bt2SudiFEOtatC7BSsSZSOeP0y5PowrDPQY18R+Da9Eh173fLcsR7XIc0sexqOGjDhiKsNRe6+tT2BWuvIwAe+skX2VxlvCEUVQkAFzbyI7q/qsnR9dqRz/a1za56RbB7BXtrWM5nmhHZ9wmAolg1BmrR1/JbKokX5GU1mNJ4oKKkxaKNJ0shdUUASYusQcXETJNsjhcRuzSX3Er/XFWkyWLYYdsY+PcbEXkwEjWe9Q4Rm6MhpTNcdQYEKUK0qll0oKWBu2nGrFJ21TQXDkJKFBOdajEmJZNlJ30JFK02WphFDqStcG3BtG7EUWuzKcvK2HX7h//z7PESZQjsAACAASURBVPeHT3D58hVcveKLwP2ntqmbivPnz7N7+QpHhyWth6kqKK/vc2pzk9PnzvLilV1+7dEL3FgZlDIE5Tg1zVB1RRHgTacKynnG7mIJJuew8qjgUM6xf1RSzDIO9q6wM58zn84pOWDv+i5Fvslx4kmyjLpaMtHQ+tiepD4Glc0pneX5fcvV1R7N8YL5xKC8pdUZ/rkDSucISYFblEync/w0lvBgS5pGow81tra0OP67n/wJ/sIP/BDf9t7vwB1a/CT2krLWQuMhE45VDO2bdjC4roMa2tHPTkwLyhC5bXgX8Utb0XrpuCogefRsISqeoihiO2XrmHQkXFFcRusIWnclOZHfFMPVCJUM7WikO0iS5D3FQBTPuJRMEgDSslvwKaEmtG1sRikwSpIakm6fSz1lK5Sc1wFSvSEUlViASD+ITOm1lH/nqUjfKPmOuNJxYaou/RoLkcWLKcsIeMfw0PfZNtH2GB07fCaxRW4bHJmJBc0qjJp/dZkLGCZchGzcqmVw/bvaxER4KmKRsv59IDKGx16LWEl5z6FvUwzhRIDEM2rckH3JsqwPc8XbrNum7yUkin24V3xOeQbBucSTic9qMWlchxdvXOJn/97PcP3VXXZfvkTdxus99dzLTLmLWXbI5OyERAVm8znlakHTOk5szcmyjKt7C373saepzCYqKdABsmbJ/WfO8Oa7H+DLr1zk9GZGUHO2CsMr15csiNSQdFpwbEsmOuMd73iAPECWOKrK86Zveye/97uf5WLpcaEiOKhchZ4UNNaSG01uKyAaoerYgyk4cB7nDRsntlgs9mlbwywryBOonKWtPUVRkGSaqq2oa4c78LRtxenTO/yLX/owz3zxAj/243+Dw4MlR0clWWpI1BDe3NyhQRjc4slK1nm5XPbG1JjYvFD2hVQWjBnqbUcKFYUg17c9LCLcJYdRvg/1xp0jJIKAoQfc8XHZOwiSXR5TYmLkMJRPjT8jVIS6GTqIDD3DbE8vkr39da31U0r9glJqVyn1xOhn20qp31BKPdf9ebL7uVJK/Ux38OjjSql3v5aHkFo/2WyxB7jpvaX4ogNmNS6KzLKM6TTvMybC8YC4GSUzKIv+FcMYgtQ2dfVWbRMzJelNhbfx2fKeayWWK/59cJGBLi07pLLleQQXqKp4UEFZln2YJild+awov0E5yfwPinpIBXu0Vnjf4n2L1oq2jaQkUbaxjGboxyWepTx77How1Ie1rY1RmIZHH/0sf/enf4YXn3mBG7sHNMFgK4sPGWWb8YXnLvOJz7zAJx97nivHmmd3Dzl1z52orQ027r6L3dbzO59/nmayQ6sytC0xxkFd8cCbz3PxpYssrOXGkeWVV3YxxnD32R1mE0PdWNJZTpFlbOc5d587g6diWUf858svvsDd58/ytjt3mLcVk6ZkFjzJ8S73ntTcWTjecTbnu956lr/6nQ/wLXfOyfyC6QSCduzfuII2OYkyOGuplKPVkflfLZes6orcZORGMzeW2xNLfrxPcrDHC49/jr/zv/2tNe6QKBOhGOR5bF8kRkra/EqSR+gJErqtr4HrQyzBfMapftkjAuLneR69mNSA1qSjvvuiJKRV0XrtXVQis1nRKznB10T5iYEfMKkBPxVSqdQhjntoSZQh+0KA9q83mP5/Af878Iujn/0E8JshhL/ZnYT8E8B/Cfx54Ju6/74F+Dn+iKOybh6iYOTFxxwkUTBS3Bj7Ow0KwJihu6CETOkIkzHG4FlvejZmbot1OD4uOTGbU5Zlr7Twnrb7vCyu0B/GXCbxeMS6Ra8oApqzab5GX5BujxImitcnmZOhJit+Ps/yXih7moQf+jNFjEORpskIXB/Cz9avZ/8kkzM8z4BZGZP3z2Gt7RS44dVXL/Nbn/wE7thxvLegdeAmo+6a2lB6T1vs8MqB49LeJVq75IVX91mWlja5hDIZoTUsg2eSaiZYGud570Nv5vkvv8BB5bCZYdEYstk2V28ccGqacfLkFtXhkhsHCzYyzdvf8maefvYJvHIsFqD9kpkxeOs5tZlx184WqBgCnzppMLbCrRa4yZyrBwdcPzhgcz7nHfe+ledf2aNRBqcsx2XJRlaA8hy3JYYM3ymcY2vxZYVJ4S//wAe4cwaHqyVNq/nsUy/wpeef5ad/+qf58R/7sViOBNiObiBeuEAT4r2KYY7yOPDlJKsq2Tk5ich7z3K5ZGNjs1cgIj9JZ1yFWGk67yl0YWPWtR8WnHMyyfv+XMMhKIN3JY385Jkl+y2yIuD8uCIBojxbO5yMo5RZU2SDMbZrpOfXMr6mogohPKyUuuemH/8g8N3d3/8B8CmiovpB4BdDVJWfVkptKaVuDyG8+tXukSRJNwnxcTY25hwdLZF+4G0bgeMsiwS4siy7FHMXZjkbexkF2NjYZFWXvSKpbIUeOY7TSb5mFQBc45hOclZ1BTqGgq5TDE1HpvOua5im49XqrotA0oV9PXfKua5lh2eSGkqpqwsDgVWs4HBMFp3QaEI18LGUos/wSNgoIVssvk6pa9vXc4knLYkIrSHXwofKhhrHblOYrkyo6QpZ29CFrEnka02nni8+9TS//Mu/xO5Tj/PAHecpU3jySxdJzDZNKAiJRycRU/HOUbUayGjCHFdDS0bWESFVojmVBt73Z+5nY6p5+aXLuMOSxhqUynFLx6FecHJ7E51qXj1ccuKE4ZqtMERv9ODoAFt5bO1RmYcmQ6cFWQq7xyWnNjLa2qKaiuvXYo93GzzbruK49DjvKI/3uO/cDpP7zvA7F54npDkTZVi5itmsoLA5rfWEjuy7kRZYteD9b7+H7PAFXr5msR6OSsddp7e4nYzf+8PP8tP/h+NH//qHODspMJiefNs0tg+1JMEz9u7HkYNkc4PqjCsaW1ZoY8izHBViv6es89iSJMqjnggxOtJ1JnIgSBqP+hLvLdYPRvqE1kUfokYC8LjIfvDeBFON3rvDE9vnqCT2kvfe93W5rvPUJfppmgilNF1liHOxR5Wt1iOQrzX+uG1ebhspnyvAbd3f/6jDR+/4oy6glPqQUupzSqnP3bhxsIaZLLs6N9nYMIQqaWooiqKn8EsYJq1OZJLkDDixYDGDl/ffEwsjoLV4WUVRDNmJDqcReoG434KnjVtxCDdFsi7i0Wki8B1aj7Oub30mzd/EUxpnOccZGck+RpdccI6E4+Njjo6WfZggzezGx4qNwc/4TglaRzBdqYTVqkbrBFAkSYoiIQRF09SYNLBXH/Irv/KPWbzwFD/2Qz/I6XnDfWdS/sp3vYN33j4hoyJzFl86tJmTpQUEg2sg0TnOZphEyjocKZ47Thbo6pBLL7zAalVyVHtK64g1ndGT3Lu2h0lyvIIzt5/l9KktslSTTDQvX73M5s4WNZaT8y0a61hZy6pxLBvHtVXFEs+urdgrHQc11G3GK7v7HFcO7zOydM7h4QHX969graUsF5hUk2aaugOwkwSapiLigo7bTxi2ZwZXliz2l6yWkRxZHx0wM4733nOewxdf4Bd+9mfRW5FPV9cWW1UdNcF04V+EDqSzgVRLSKscKQyOCi6u7Xw+78P8ccpfQslxJwNjYnPBHmMdnfAjsjmZ5D3m1LbxmDpjTH8kmewJ4dUJEC/hnnAGZR+MO56YTonS8RuNHvpUybOKAhwXM3+t8a/cj6rznl4PG16+9/MhhIdCCA+d3NrqY2cYatJ6yv2IJCntV8f8HuF0iMWSUE2Uznw+78I2O1I6Q1cFWbi2jaUh0rNn3N5joC7YHgMTF35cMydeGnQN/JUmzyILeDqJf8opOioMIancT7o2yH3HdIeopNQaIDnmQhkzHLQgguB9wLkW70OffBCelxzcIJtDAH+TGsrVgp/8r/5r9P4e77lrB98ecPrcDnfcfZbzt2/x4Jvv4d/54Dt402bLqbTB10fUdoVra0yqcG1FmsX7mSRHq4zg4fbtbZYHBxyXNVprjq1nMt+KB3JqqSOc4xxMNzY5ONjnaLFPEiyzIufOc2fItOb20zsc3lgwn2/iWk/dehSG5XHFYmUpG0/bUT3K4yVkBpUaSIhESJVj60hcTE3RNx+MBdqexGjyaYYPjtqWJGTUK0sLZNOCpi7JjebENGeWGPJQcdJ4jveu8LGPf5TNU9sUs9gV1dshFJdmgsIi1x0nDujDPTnIQcL0vtrCD2cDAF1YOITqch0VfG8g02QgZg6QgByQkSOHc2itmc/nPcQi0Mq448JYxgQrhqEoWb4TnCfVhgRNawfKhCSNRMb/dTTOu6qUuh2g+3O3+/kf6/DRgbQ2pENlQvvGY53rOZ0WPTlSPBoYDtcUvEipUTuTEFsKi1IQVu641CWy1oeGadMOWxLlOa49HAuYHIQw1OQN4GZZliRKY6uKzJjeiwptpB1o1suCxqGhPMvgHUEIbZ+5Gfd0GmdvmqbB+9DRD9QaQVOa3sVi4nWel1w3BM+pU1v8zN/929x/+iwPfdO93LaZ8+jjn+bS/oLnLr7EU889z+HRAnt4kQ986wM89MBdnCjAtRWJ8bi2Yjaf4NpVBPWdxjWGutW8/PIldJKhOuWgs4y9/YOO01URiIDualWyPF7ivePsbTuYBNqVxZUVi709quWSSZqzXJYobahqiwqxbCRNM0yWUxQ5ti6ZaE2SaVaupHEV0yIqp71rC9JkjlY5k0m8fzQAUFVlLx9Jojk4sByVnrqBa4sDpoXmaHnA8dEyJhfqkp3NOcfX9/jM7z7Cbz/ycJS/Nta2icc7lnfBfcRgijGSzLQklyQqiHWwwzl+44M95LpKRblKlGY6iRxDURSS0ZU+/sLrG8jB9Ifqjtu4iCEbg/3jXm2CucqeTLTu22WLKoqlYlKvKhnM1+7f/HEV1UeBH+n+/iPAr45+/h932b9vBQ6/Fj4FoPXQPcH7uJiymYwxsbF8gLaJ4ZOcSwfRM0LFHlUqgXRiIqHMR2uSdGUFw4EJ8WyxprbkXewti621JstzPPHcvWI+hI3jtjEDz4m+B7d3sXp/jAXM53McsecTxpBMMkKiaYInJBrrHc468DCdFIQ24jwaYkbNeZq2a0Fb10wSg9G6P4vOdjSCqIhamqbt52RcQiSh6RjDSpIIsKcmtkDOJxmNK9k+tc3P/9zPsrz0Eg/ecYYkOBbWctep86jDBe0qtoRZlQvKKueVV3fZmmre9+a72Mg1dWPxSczUmUzwvhalLR7HKttkRQZNxSybob2jKHKq2jObxTUqyyUmi2cpLvcPOHtym+a4ZO/GAp3OsSHWAbq2Yr5ZULmKyTxHmViFUGQ5oXYcHi4p8gyda6obniKNjGkXPE2RY42mChajPdpDnmVkSUYSMpJJTktHKUlzzp7eosgMB9cPyJKMw4UlyzSVPSBJK247s83pjYJNo7n+8h6/8esfwRqoFDAZTu8WwzOk6CWLO3TiFKrMJM2xqyr2uApDBYNzDrTuWi13Zwgg+8fTKmgV1K2LdYcMLPHptIhHrreW+UZBwJEEQCIDIFGetqliI0c7RC5i5DRRBqV9cr2q+nP72sZROUvlLKvG4jXYpsKk8ah31HAQ7debnvBPgd8H3qKUuqSU+uvA3wS+Xyn1HPCB7t8AHwdeAJ4H/h7wN17LQ/gQerwI1uNd8XQGTonpwqYYfydqyHL5kfUQD0f4ROOUrJwoIxZpqNUa7i/4jlxHLIk841AGYfuUs3iDERAdjteSkFSeU5SGAI4wnKYieAMMfa6UCshhF3JyiKSAq6omnjQ9kFwl+xOB+PUTbpIk1ikKHjI+U1BNDR//tX/Jk49+lu99x4M8d+EL1NWCb37grcxObHH27Fnm83mfai9rS+U8VRNrAjOlKZKMpAVdA8uSrKnIqEiVYtV6vvTqLmXj2dqaxwNnQ8vycJ9kYihLSzEpaGuLaR3aeZLgKQ/3yYieRJtoHnjgAbbnm1QdMH3+9nMYYrvfWZFzeGOf0EQPq7YQGk0yIbZraT2zzW0uHxxQulg7ONFg8KjaktqSE1hu85Z/6757uH+j4C1bc+7ayji6tov2BnsYw9PVynHu9vPcdttZ5hs5V3cv472nWjnqg4pf/se/yMntOda6NTgjlkMN2VfJhol8WGtRHUlXp7HVkOBGQr68+ZSmMX1FWiLFMhXdc7BErhSGLM3RyqDVQL0xxjCd5l32NyfNDGk21KdKuCgh6nh/jWV7vFcHOR66zMp++7rSE0IIP/z/86vv+yM+G4Aff81374ZCrTXXlzBEMmNN109H4nDVUQoIoPx6CFjXFZkZuCPj+Fn+Cx1oX9cVSZoxmeie5CibXTwoKSYdp3DldzF+l0MchmOrBPcRt1g4YKKwhE5hjAHlaVzkvzTOYUyx5mKrJEA3P2kSOV9DPaPCdEpVFJ0xpgfZhwaE9Ira+0hiHJdGxLWDqxcv8Qe//3toV3GiyHjvQ+/kmWef4JFHHubM6fPc2N/j9Okdjo9LlsslE+3Z2txi76gimRUcOovSGUm95M/cfZ7btu5FTzTXj5Z84Q+fJ59OaJOCZ17Z5TvfcT9p2KeuSzZmBZWGE9MdlosD0uBJyDi5MccdLwjWctvODi/sl/zhUxdI77+Xg2v7kMYC2qosOVos4uZVYPIcNfEcO8fxakEeDCYz0Dq2NzY5ripe3t3HdxtKW8e5BM7dexc68ZBAMd3kxvU9tqYG7yyL60smE0OtDCq1lMfRI3n55UtolVGcMpw+fY4LLz1Pg+Fwb8n1E1f41V/9v/lLH/yBmJ11Q/8zKZUac5FADpY1qNZTtzHbZ9vI6xP5rGvb86NkTKd5D4oLbiV1q2IYBWuK2C3QNYE00FV9RJC/SAsqWzEtsojZ+aHOdByBxOcdYIhm1Ate3hFiRZT8bDabU7mqzxS+1vGvDKZ/XYaC2Wzep0p1zJNi0GgXaQvC7/Heo4yGRJNkBt9VkvedDdTQZG8Mdo9ByLpxqMTg0TSuRGmH0o7ExHYcaTKEjzCQKwFMqnGtBRWPS5J7JYnpFMPAtJ92J8zkWRZr8qS0wchpHmCSDK0M5XEVz6LrFFn/Pp3RmUzzGEICzrU4144O6owMdyEZivUVzKKqYsa0cbFtSuO6Y+KJzPy6dRSbcx7+7Y9z6YVnmeY5jz7xBXavXyQ3mm++536a1rE5M+zvXeHgYA9t4Nydd7FYlUxObPGpxx7H6zk0ju95z9uZuyXXj66wt3sZe+OA9731Ht59153kfonXmt/+wkWul54TO5sk2qGcobx+iSLX+ASOWsfuwQFmusn81FmC1tB4mit7LHavsLkzZ2srx0w02axgsjFnc3OLNM9Z1iVXbuyRBUh1gXXQhKjATWrYKy1lkYPWmHJJbire//6HSFPHqlxw/eoVnvrSs1ypHK82jgNvyaeGJjNUGubTHKcrDvcPmM92cFpzNj3L1uYWR6slwWvKY8uVl/d47NHHuHT1IrYFVIbShkQbArG1cOvBB6GfRE9Ga42aGCbTHO+7bphhCOnF0EVOVPSavPN9dtk6S9M6tNEUnQcsUcVsVtC0LnaqncQSl6PWUuFplCeZZLQ48lmB82CygizPYolZl7KWkFUSQDBEBEmiSZTHaE+eaTSO4KGYzimmc1oXI44sy15XBu6NoagYCials4F4PxIaSUuW4SCBaI3ksAJRJAJwC+goYOHNJ7SKtjcmwzkfi33VwHwfmMPrbrn0CpdrSVpZmOYDOD6cYydDKA0weHjyPGLlJOSMxdWhd+erquqr7SVZsFpVfSZUslZDVX7omepFMSWEgDEJEHrW/NAx0vHYY49x4cnnqSvP9b0F29vnyDbOsqg9lbWcyA0T5mxm2+xsnqNewCuvXCZJCy5dPcD6LBafat+1JYln861qx3FtuXbjgFluOL0xZ554Ul/RknOwLDl5YoOtaZwDCWtkHV559TKXX73MM89fxHvHX/3hv8bJnW2axrLcP0A7T7sqMR4O9i6jveXELOfuO871HLbptGAzNZyYz2mC58ZxiasrdHD84Af/LLef2ObTf/A4l67uc1jG2r/IiYv0glOndnhp6XnqygEvHy4xW5vMJhn33HWOTFnecu89HB4dsLe71/Upr/DEzHG9qvh/PvZxPvPop1CpY2VLDo6XXSgGSRKL6cXbHh/bJmC6NKkTAyQVGUVRsJIz9zoPej6f9/Il/dlk/8h1JTMsFQ3TSc50kvUHT3jv1/h70jlEWhmPKz9E6UgiZjjlO9ZyxqaNg4KVVkRKafS/hqzf13WMMx5S1jEoFL7izD2xLEJ8FJwoBPqSFKl/kzBOUq7GmL4NhfeexvrYHN+BrdeP6VrrfZ4MEz6mTQgdQr4jylOKNEVB9mFnx24X5SYutAjcUCZEf/Bpz0/JMuq67nkp4o4LT0p6eKVp2rfKAdZ4LM456nrV3TcqvK2Tm3zx8c9yuFfRtpobiyUPP/IFPvpbn+PZK0vqyZxre/vkmxlmpmlUxc65bdSk4NNPvsCTL+6xXMEEeOD+8zTVAU2iaZxG6QxvMlqT89KllzhVTHjwrnPMsFx65Qr/0Y9+iDvOn2Uz931HSzmZ2baO6XzOiZNbnNyekxUZr1y7zINvf5BplqEbhzsuOZHn7GwUvOX+u5jnGa6q2N/dxUI808+WTBLHqZ1tLl3dp/IeYy3f/q4HuXH1Ir6q8ElOqzNWDlYObOOZGsOJPOPyK1f40kHFUVJwaWl5+tV9JlnB0XKfifG89OzT2GA5fXKTzTTDS4lL4zjY2+faK5f53Gce5qf+1k/yLz7+EVZuyYmtOajYqkZp34dNkvWVBI5k3CTskkRI28ZM9vjQWcG3xGOXiEL2jQD6q1XVKxYfOSl9wqrpKDzjCEZKsATvFHw0TbO+lQxErlXMzM/R2vTGX7LYfQLKrB8I/FrGG0JRKaXWgGVp0TsGrsd8KmmtIQprOGPM9KCfLJJ4WgLK96eGdFmxcf8qsSADKDkUVcKAeUnr1fSmLJ8siHQjkEUet3uVAuaBdxWvLdiCPEuaJv08DKTOmKIWayl4wXK57MFZ4dTISb3jY7vlHSKfp+6v+dRTT/D0MxdYLpeU5YKqKSm2tnn20j7PvLrg45+9wIm738lnnrvMI09d5Dc//wLNxhmevLzg0sJTh5zEebZmGW+77y5cvaRRnmpl2T8sKRvPflmysI5VaTHB8E333Y+2Jf/Nf/vfMz+5Q+OH4+OvXt2N65gYVlXFtf19jrumd1cP9njmhWfRynHPffdw+vYzBA0k4NCYvOD28/cTdEHdRi7VtMhJck9xapO0mNNWjru3dvDlAV++9DzHyhP8cPhpPsnwxCOsgo3p/M0UVsslIcl5abdk2RqqVmO95/xd93Lyth2uvnqZFI/qit9b51Cth9oxbeZcePQJ/t+PfISf+zt/m//pf/4feOzzn8U2JdNikN8x0VJAZ+EECu9NsFBREmLoRc7HWJLI6zhcE/nvWxz5mIgYGO2DQyD7QpwIyTyK/EqkMZ0WPbUheLoTwWFWzHuC9jgxFlsev/bxhumeIDhS9Fo6wpmP/Z8ELI/hVNXzSCInpKsrClCVsU5KAG/xgERhiEa3LoLyiYKgNNa6jrtT0QZQndue57GsJmJBsT1wog2JzmIfbYaOjnIQY9PGRv/ex26dVXd8kDaGtkvliuISBRuC9N92TGdFF14qWh/bFmcmpnN1CDQMmJjvfi/eYyyn0LSuGbw/YsvhxjoC8b17kmnbMploHvnUpzi4fEDVVkzSnISMo2XsfVWvHFeOLf/wY5+i8pY0yWhWGR/9tcfwxlBsRHA9GFiUS557+QqGjFVVRb5UEmvOUg0hy3FoLrz4Aps723zwu7+DSy+9BCHnTe/+Np787GfY3ioIzlGvYFEv0R2fyK4c1ls+8+kLvPc/+SHOve1eLl1bcvXVXd583/3s7x+wv7/PExeeplpastRQZDm+LpltzDl1x1l++9EnWDQZJyY5862MV67uoX2Bcp4VkSZilEY5z9ZmgaoXbJ8+yxde3KfUsSQqWMdhgN/78mX+4tvuJc8qrl5+CYqcN50/x7XFgle/tE+SFCjvOV6V7B4suMcU7Gxsc3C45NKFy3y5vcgTn/sCZ+7e4U1vvpf3PPhuvv3b3k9ZVgSvuXFkmeWx44fWGtsuu8aGNx/M20EI2vcE0WyzwDsf+XujppLO+3gsWqrxOmZAGfG5hB6BEDR9bAwZ5Sv2cl+tqpjQMaYzylUvg7pr+rjqkmHiXHg7EJRjFxLHZFq8Lq/qDaGoIFqT2MYlnhosXQghdr2UvlKieESjjwuMZaGk4TwMx1dJ6NY0lrbjnmg11PQtFote2QjfpG1df5SWc5GRrmEEePveDR5TCqI1LHti3ZDV8SOcbTheSLw+pWLf8/huSedJdkTYxKAS1fe2bppqLUwcZz5hIMAGPI2Lh0pOJtkIjO1ATZPw9NNPR7yrsbjaMQm6P9RSGcOqsaAgTXJoIdEZtfUkeiiszbKMYxt7Rm0oD8GRuCWzTHO4sqRdVlPCldWq5Mtfvsh73vVu/vk//zB/6a/9Fb7lO76Xxx59JJbLtJ5lE9Phx2UZeVUq59ve9RAnTmzx+7//CFd3F+ycPcvl3V2+7/v/LE1tIx62u8+163s0jeX0ZsFb3vIAH/7Nh/FJxjQ13HPbNqZ1uNqh0IQueTHJMuqyRKcZqio5fWqbG0cLWu2YJIbGRjlalRWrY8fxYoEv4nl9t911D9f395gUc3RaUVaxwZ93MUFz42hJVhSU1/bxqivRKS1fevIiL794mcd+9wt8+Jc/wvve9628613v5sH3vY/dV69gK0uWdj3IiMoh0QbbVL33JAa+b31cW1RYP+BE8CSlNIuqxGgdS13oIJBGCupdBNmT2Dm2KAqOj8vOK5JC5SELn6YGZYZODHIo8Jh8LeVhcqhElPXXpx/eEIoq+Aga990vu5M4JOQZlw5sbGz2mFXTWObzOdZaVqvINheuosFY1wAAIABJREFUhzB/BbeR74/7SMkCjtsFw3AiLUTsSKzBdFpweOOgBw+lS+bGRuxAGQFC1sJR/NALPqaHoyclIOm4f5BgYZOJwbmmL2D13tN4iwqqO75r8ELlXcWjGnef8F6IhLoD3ddLKezS8qVXXiYzGar1ZKpL4ztHPs1xlYPWsznbxHoPVUXwoIhZV5m3vquDznjmpV1m2nHv7Tv4ekFqcmYqeljOw6w7wKJtW67u7/GFxx/nrjvOUy1L/tE/+VX+7b/4AaobV6iO9nGp5vqixAWNR6ON5vEnnwC74PypHTI959T5czQK/smv/DMO9/dJg+aoWrKol0wTzYmNHbZvO0+YbBGcx5cL3nT7m3n+2ecx2lDaeHQTfmgfrJRmlhuCMlzaP8DpDN1qUh2zazrARl6QJoY8N5S148qre7z5vnu49uQLsX+WyfCNI5/AwbXLmNOae990P/uLJYvFAuc9ofQYZahrcNUBB3sHvPLSJX7rNz7BqbvO8c1veYC3v+3tvPud72S1qrpmjhlN49eMknQLFSjEd40VVfAobZDe/eKFz7uuCEIaFuUj2eO252vp3uBFbLPqws2hU4j38cASY4bj6sou8SNKSoX1FkPSmuj1gOlvCEWFWj80NDF6ZPnjQQUQN/FisaAoih4cF+6IaG05v08UlGh3wWkEc5KSgKAGTyxJDLaRzqIRYxxzRlarcs1aTCaxDYe0aYHhHLYxNibYlbRLBql30iMhWSeSSsmDYAMBQP7shhRCSxjZdtSK6XRG28bwr7JVp6QdstwSOkyzKZ//g8c4unEQ+zCVJX4VPSlvNC7Ev7ujEmqHmWb/H3XvHiXHdd93furWrerq6p6engcGg8FgAIIgCIEkCFEkTVmyHjStlx1Zx469ju0k3j3OZnejbBwnm3W8XkfO8W6cZNcnaydebdaJY1urlW1FphRLll+yrAdJURTFBwiCAAgMBoPBYB49Pf2orsetW/vHrVvd8DlZk7vePXTxzJnBsGd6uus+fvf7+z4ohKE3yMoOd5zI4hYxuesz0D7Xd2KmZtocmGkj93ZoASPho2OzqSgBjVqNXq/H/MI8b7rrJA89sMmj734PX/3S77Pb2yEbDRAFFI4k1SCUwd6+/bH3cO3COZTW9Ho9Hnnrt3L18c/QboR87/s+yD//xV/AlYIjCwtMTzf5pV/9ONTa5EnED//l7+Hl555GBwGDfs+kxSiDY0WDAXU/QLqSPnDx2iZDLVGFxClUyeeTOFYmUpS0EQSjkWL1lXMcaM9TxBEZA+baIW978Cwqi9ncTdlYu0hdpHitgM7eDjg+SJ9cA5mpuKMsIktj9joDbr26yktf+wq/PzfL/PIKDz30MGfOnCXXKUU6bjpZSMPiW0pQKS9EuUlbDaHlBqpcoUpng+EwqhYPW53Zz6NRfFt4w6RebxJDticLizPaY5+UkmQ0NgEcd9vHsfSv5XpjLFSA55dHKRdUqqsJLTAmYE5Rrv6+T6/Xo9lsIoR5M43bgl/tGllpY5ymxuB+lJmqqO6HqNTIUnCEIbnVDPPXdQVOOdHzTKFzTVDzEVKWC1TpEqrHi5DFeiapBr5X4m22Q+mNRcWTtIbJG2ptNPLcLNKp0rjCNYOp0EYzJSRFXiDyCZtlzIBsNpuGx6IUaT7GrIIgQEeYRSUbuzJUPl9FxrVrazj4uPg4XpfG1CzZUDPraegPWDgQcsfRJXq3Ujb2O6RJSppL9ro9/FZAiiDFWMTIvIkuYnAkvdRnb2PAtRsdjrSanJhbpL+7w0gIisAncTTa8dnPYoZbW/z+F75Ast/lR//2j/OT//1P8PL1dU7eu8jz3zzHVK3FwFV0RzGtZpPLV6+wu7vF/s0Njty5wqc/+5tQg5mpNr/1+CfJckVYCzm0uMLhO47jPL+KIiZLFV/4whcQDghpjv31MCTPNL14gJMrkmiANzvL1WHMSIOUgYks0xqnkCB8kIoiixnEA9LLG3hzszRcSfvgMS5dWefsHQtMz7Xo7vfYurGFIyUz7XmW6gGbm5vUagF3vf0ML16+yDdfWsV1A1RiJrp0A/JYU6CItODi7iaut8XMxTXOPfMMCwfbHD68xNvf8xinjzzA2vUdPBmYiCygJjWFI8HzyXKI85TaxGab5xpj4mJsfiY3UTuOLRBvidd2Xk2SPW0MmNaawjHeqX5g5lwcR1UHMIoG1EpcyvpW2ci3SQH/n3W9YRYqe7bNshS/9IeujmaFpuYH5CU2ZCuZyTd4snXqleC3HwRkZYkcBAHDfoTn+jhW81e6IKLHR6KwTH6tgHhUlewxSaKcNPqzanIwIaV24bEfdmEDKp7Q5Pcsd8xxBCI34LlSGYLSyrg0Y8OBQowlQ2Mmf1y9D2OCq0Ov16coCobDgWFEa+uBlZndV7oIR7O7u0OeKnNs2unSbrTZ2djkb/z1D7C2doFDS7O0ainHm6fY2dnh1D33cvHSZXrDlOdfvsAgF4wcn1QZIqx5aQolU3Kt2B51GV7vMn10meHmFvesrDDa67IW7ZU6toD9nS4//AMfZPSp3+Gj/+tHGYwiwvuOVxOn5kuC3OBwv/mbj/Oubz3D8TtPoByj/1xaXqK702VjY4NaLaCfxHzzpfM8/sdPEUwFeKR85B/9Az77qU9ya3uTODFNBoGgKBShlBBIao0mnWFEoRTNMCRLTVPEcoCSVCF8aIQBBZoDCwso36e3s8Ot3R4Hl1bYevkKshYQxZpBp4MMAqa0II4N3rO/3+PRd5ylO+xy5p4zfOWrz3B1YwchJQMFjvDxXU0/iipstbPbo9vtcePGBhdevsL5i1c4fvw4x+84wfvf/wF6A02qNEkORZyW/CxJLZBkibqt+rJWwdYJZPKUYBowGscxobVmHvqVkmLysVqPu+JFMYZJbHKzVWRYioWdL5Nz4bVebwh6AoxFvtZVcOzNc7uDJ1CC3UF19p60B06S2Ahjq/9utwC2MgJbijq50QzGcYzjSvb2usDYrcHsEGMSar0+Po5ZspulQtjS2Z7t7XF0rHVKsQpyqwO0N9D6+2hleC2+NFWkMC+YIte4zhi4t4TIyYFg6RQWDzOcNA+tC+I4wXVdsiwrgdWA3Vtb7GxtUg99XF8gnYBmEOIKOHnXIpqIR976AM1mk7XrF9neWOOOo4usvvoCrtPDjzt8y11LHGkFyFwbQThGTJzHPZY8ybccWeZtbzrNHYcW2LuxaTyoHIW71OZd3/YtTNUDmoFPFsV88jc+gVQpKo7x/TYzc0vUgyZuoai5EPjG3fXQ8hKHjhyj2ZpH54Lp5iwb17bo9wfcf//ZCrDdS1Kcuk9eaM7cvcK5bz7NzRtrJgBWBiaWypXUXQnDlJXlFfpJSjdOKZRAZxCWKUfCN1QJrTXxMIIk4uDiPKM4IhlGnDx5Et/3uXZtjSAI2Li5QxxrY+oXtNjd7dDv9zh8eIkgCPjSE0/gCM2JlQUee+tZHrn/JHnSY7rpGxkPpYg50yRDhYMP2ieJJN2OAeGfe/IZnvqjz/NzP/33+dzjnyBTEY3ZJo16aBxqC02amMXOYq55rvADH40xMnQndK12PIZhiPW/qiCSYgwZTHIFJ3lWk55qk5ZMk24Ldq4LIV6XOdQbZqGyZ1/72U5ES6gcR2jd7i9tgXYwkzQIjDQiyVKTYsy4bWtXerAJykY5Lkug3PFEeaQ02WQ2QdhiYpPgouWQANX5f3KHsJiVBd211rRareqmVh1Nd2yrbJ0drODa+AqJqkMjinFku8XlJqsze9yd5GhZQN5qrSyGF0URnd0t0Jo4joizmChRRJliP+rRmg+5++6TPPnkMzz5xDMcOLCA76ZcuXieeDBg91aX+nRAqyE4uhAy7Zu/P/ACXEcwP9PkrsVZxLDHcGeTOI5ZaTU5fmSJ/ijlqecv8O8f/xJ+2GJ3v0cwFdJN4JGHH+beu04w5Qn+8I+foN2exfckzbokL80Q+8OI8xcu883nzhENU+ZnFqjJgPvvP8PeXgff91leXiYeKRwCpmo+3/He9/HNcy+ggMyRpMpMuFF/gM5iptpNDh5ZYj+KQUvQRvI06PeQLowyk1YkhaDuB9y5soTWKSt3rOA5Pk8//TR5rpiZaZMkKfMH50FAVqR0e52KmHnt2lqpt5tHOz5XNtbYzwZ8y5tP8+i33MtU3uHbzx6jngwIiph6ADjmOFURkBGMMs32VpfNm10GnZiXv/4E//p/+ll+7V/8LC+df4pmUyAcjaNvFwnLUjVQOCZNyfXGDhu2WLCkZjtX7IJlcWFLoJ4kYE8eG4EKn5o86dhmUUVCfR0VlfuRj3zk9awn/59cv/iLv/CR7/rO7yyJYpJGo0kUDctV3CUtz8rCdUmyFFcaRbL0XIQAKT3S1LgIZFmG67omk670EM8Sc9RBQF4YK5SKmUthuCWFxnM9lMrI87ySm1AUSNfF9zwTleSYnShVqTHQ10V102q1mulglh/GRz0pFeVuRWcwPlHmxqVpUn7tGBBfCFzp4giBcAW5zqgFPnmRkzsFjmP+LjOQCtI0qYzP8lLfhePgCAEO+GWFFQQ14niI9eZKk5it69fxCsX2tVUCz0c5Gu2CF8DGzU3SnW2KvKA9PQ2yoBGGjJKEWCmmZ2dI0gFxoVi+4wSXX7mKk0scESPclAPtkJrv0I8N1tOLBgxUwnAU4WhFO6xz5OACe/sDgtY0N7Y79IQi6vSYDqe4un4dp9nm5HKbjc0b5F6NOFVkrouvC5LtDgsHmgwGeyRpjhQh6JyGH7C93WXQT4mSFJyc4SCi6UTsbnVwnRAnd9EyJfQcpjzDqBaNWZ568Rw9lRM2G7h5RiA0oXTJgDATKJEzzCLqKL79wTtI45hbt3bJ84yl5SP0hwn9XoEaaQZJl4wGWvs0PZ9EpQTa4eDcAjKosdHdY+XIHQz7KVNhk9zRLB1a4IH7T+HqhNN3HsYpUq7vdIi9molBU7kdkmh8FDCIU3b6I5p+DeIB8dYGNy89y/rqRTKhOXH2PvY7+9TDOlESgwCdFriOxBUu0SAiL3L8mo8uNK50Jzb/ogLQbTWUZRlFUZRkZNeMZUfgUFTzTWtFrnKk6+EgyLIEx3FI06TEV011/9nP/S4f/vDf/pnXska8ISqqoiiqdqsQYkIaMA7v/NOrtdU92crIHstmZtq3sXotlWHSkM5O6klzMEt3sKzgSfqC1fEFgcE08jKiW6XqNjGmteGo8KayUrPHPOswCuMy2pJXx75E46DKsZTBmpeNb5dd6MIwrI6mtv1rNXP2ucbVoQFAjR5SsrffJUlSjt9xjM7OFk4ZF1VonzuOHeed738PU/OzLCwustCeJS3V72E9ZH1tjfmpeVwFVy9c4PQdy9y73Obe5XneevoEoYrZvdmDXDJMNMoJqNVCarWQKIpJkpSNnR6F45CNBrzp+BJT+Oxu7zBzaIFMClQWc+7lixw5ssLsdIumEBRJykinfOiv/yBOM2B+eRERCLpxh9m5NldWL5MohV8PKtb1z/3sR0yTJbMdMkXNkcgChnFMhuClzU1j+xKnnDnYphmAzhVZrkkiY29cCMF8KPlv/uYPVseZ6ekWvu9z88YmpCnz0xI/NCJcpSKQMT3VpdEIGSYxV9dW6fd6qFHKc889R6fb4crqKloIelHEV554Ctf36XR7PPLwGd525jjLXow77BAUMdI13WhfGE6fUorBYMCVjS6v3uzxyo0uUerzlS8/ze/95qf4V//dT/CHv/sJAhkTesZxsxbI0rk0xpVjCZetuCZ93Se918y4Gysu7Clg0jrbnDyoNk8L50ySO61r6P8fxnl/rpfjOFVpaLlJY/4Tt5WaVlBpy+BJrpTFkyxQ7pbVmX3ceBGhogWMW7tUxzdLZrOAteWLZJmJFPdcSc0zSTWTXCx7LLQLisWObAlsF6dJ+sTk81hg04L245QSXf1djkOFydmfqZcsX2stay/bsraWLhb4t/8eRYa68PCDZ/mRH/p+9Cgi7g0olOaVS6uMVEqz3eLuN50iiiJmDszT2e8aSsCDD9PZ7TEz3aZepsrM1BV1EbO3tUEgfZCC3DHhrUKazEVD+fABYRwRCvNedXY7zIZN0kLxuT/8PZRWFFrRaM2armU84G0Pn2W2NNf7pV/5ZT7woQ+x1+/Smm1yz70nOffKC7Tn2jhCsL3TMbIkR/C//8tf4uqVdWq+yYYMGwEyTREFPPaeD3BxYwfQHJxp8Z6HzzLnQTtQBL4gUxDUApwwwBcw7QtuXj1PksSVVClJUg4cWkR6mqDpsxNF3OzGrKws4zlmE+v3B4jAZ35p0dzbJDZGi56P40nWb27ih020JxnEKUG9yebWJqfvWOKHvvMxfurHf5Q3n1xhpgbTviJLBujUbFBBPSTKBP1U0EslX3tpjVudlJs3Nune2OTlbzzFR3/+n/HiN55gaXG+lKDpssKnipSzm52FNezrA25baCbdPexlx92k4NnK1Uaj+DYwfjK38rVer8U474jjOH/sOM55x3Fechzn75Tf/3PL9nMcpzq3WjHuuG2qK12e5XVYDMaCxrabMBYLj3klFggEbqvArDp8HEttHmOBcbuoTO4WQIUXFbmqiGxjl4Uxbmafz1ZRlqVun8N27iarNynHwZP2ZyYrTbvATMYlAdWAmFzYbGVoGcmWDOs4gkajaWx1Ck2/P+CJr36Jc889w7vf/jA1H3wP6nWfZ598mgMz8/z2px/HnQpZXVtlYXGBu0+cYGtzE4KAzmBA0PCpTwlac038qZDFoyvUZmdprcxT1DSNQBIUJQtcg86BQlArlFEHIBhk0B30cDxJGIQEhUAlMbHSnDp1ijSJEQEcOjBLw5P8jf/0R/j6157m4MI8B2faLEy3WFxaoD3bpnBA6TJkE/CEwPMC8rx8H7OYhelZ7jx6jCe+9jRuIyBUEStTIbvrq0RZzPd98D1IKZC+Ad1Hg5h8lPLutz3CXSdXKjqMvacb+1vMH16klyg2+pKtWHDpyjpOqqnl5v3vJzGrN9bJU8Vcq02eKzY3NymkYHuvyyuvXmFmfp76VJMbNzcZpqoUc29y68Y6xw7N8rYzJ1isKwIpCOt+uagYrWKea3JHMnR9ulpws694aW2LvB9zc3WNP/79z/HP/+lP4/sSrRWOY/h1kwRnS262m6cFxq38xWJUkx1wS7Ox5pB5PnZfsAZ+Y5xq7Kb7eq7X8mgF/L2iKE4DjwB/y3Gc04yz/e4C/qj8N9ye7fefY7L9/szLTlbXFaAVrgNapXiu8X/ShSLXKZmKDaicKlwE4YRIt5LWCONnZR1A0SURshbgubeT0+oBpEkP6SpUNsCVAkdAgSbXCuFqHDEWMGuMyViqjKeVDANyB1xv0rAsrTpPaGVYz1lMlkQ4OsYTirAmII+qm20dPK2xWnUjC0GWGpcHV/gorUhVTJanFOVubdnrYWj8r0zijZFRuAUUmaLIFMI15ELrMV6rB8SpouY3ObS4wMqRJmeOzzPc6BDWmjzw7g9QeD4/8L0fxBeStz7yDhYWF+gnEYNMM1cPaE3PM3R9bo1SrnUlz1/u8rtPnufJK5s8e3GTg4tLTPmSmgNCCgpHI6SxjB45xpbZLYzty3QgqBVQZCnNhk/N98tIe0VOhFdo7j62SCAEv/Xxz3D3m06ydGiF559/jr39HaTweeHCBabm2whfUHiSWitkZq5N5igyUnKd4mvoxvClly5zeXeHGQkPnD5Dvz9gkKbs7/c4941zeEVEUXTxAkFYbxK4it7uGq9cuoIrNYePLBAnA/warBxa4eIrq1y+ukFfp2ih6GSaE2cfwPM0e/0dpIbF1jyNekg8iOh3I1yvSZL5dG526WxtsrG2SStc5PDKSa5e2uKl5y8iw5Ab25s0ZkLuue84b3noNGeOzKL3e9TilHqucRxFKhSqSKkpEBmkGexFmldudrmxFTHai5ga9vjffv6nuHzuGSSS0TBlFNlcAo0QErfcQBxXUJS8qeFwUC1OMD6+SSmJYpNGXqsHOK5AuJLpmTaOCwhdhaEKKQ1GVnYcX8/1Zz66KIqbRVE8W37dB17GRGB9NybTj/Lzh8qvq2y/oiieAto2COL/5jmqI5j1vwGqCWwjpofDiEZjnOfnuuZ7Fl+arLomrS4svmUXECuA9jxJrqAeNKEQ+F5Av9+7jeNkF0FbBdkFxLLe6zUjGFap8Yx2HNMxNJau1kxvgo3rSka5Jik0sR4n2Ngq0LwfRmJiqzy7GFkvLmvSZ98ve1kczJJTsyyt0nKTMlzAHq8n8b4oijn34nkuvnqRI8eW+Cf/448TSMUv/MK/5Vc+9jidNMBtzVPzAvr7PY6urLCzvcnzq1vsJJIXL3VZ35Y8v7rBXqrxwxaFkvhOwNeevcBWrOkWgnqjRb3RwnF9hqOU0AMXRU0o6p4wNjuOiVtKhgMaNZ/9Tpe9vS4LB5Y4sDjLnXct8+azpwgCzY3rG1y7ts5bHnqYL33lCdrNkKNHFgmEpogHTLea7O/uMBj2SExoNM2Gz+yBeXaSlEwp3nLvaVYWZrl06aLxr3IEcaI4uLBIWG+ic2P/o+MerWbAncePcXx5kV6vx/b2Du12myiKGPQ73Hf/WXrDFM/3STNFDjz33HN4nqTdaiGFYYGPyoSZWs3oGD/84Q/znd//wzTbS/T7Ay689CyerxkMBzjC56WXLtPd67F6dZ0rV1e578xZ3vzwKX7w+x/j/d92hrsPhkyriClHgrKwwtghZLsbsd2NuLS6xcW1Hfyoy7Nf/Bz/4Td+mQMHQ/zaOKRBKYVbjh0jhKfENcd5mNZWyJ5ObEVl54UtOOyYtrQdSxyFcfX/Wq/XRfgsg0jfDHyN15/t9x8PeSjGL9BxyorKHRviWzxqaqpp8BrGZvW2JT955LIhjhVA7YiJik2aIIjy2FevyWpyC2GORRZbMje8BNVLvkmajjlVWmuyUVzxnQRWeJlWJoBSUJXNhkAKwpWoHDKlqUm/wqksfmYXGYOj5eWgMKvWZEnul4xzOyDtYDOcs/FrTeIU6fsmEKAUNZt0E9My7+33mGkv0j6wyNb2Dr/x8Y/x3e97F7/yic8zEoK/949+iSOHW6yt9zh6bIGt7WdwkIQy5Zs3OujCpyYk/UJTr4UkccxUo0XU7xFOz7Ox1yMMfPpbO9WgD6faDLs7CAcK6VCvSZQGVwqmG00Ozs+x2+2ROpqLr1xmOOrQ6XVxHI3vge8q/uDzf8h/8v3fw1TD5/t+4Ae4/PI5alKSpwPmWi02e10+8OjbOf/iC2gvgEIxM91mpDXKBVdo8v0O/V6P3DE2xqNE0Qgk589fIM/BMqmbNUmURly8us6JxZCZmTZBLeDG2hozM7MgYPPWDv0MRmjTRdOaQRwxVQMcgZQhhQtRagi6uQDX8fnq157i1JlHWFxa5oWvfZGrl88jB13uvvckgRsQZykbWx3uu+cUx46f5OvfeIFjdyxy+PgSX/7CFzl5xzzHji7yO198Di+cRU9sjkkSU2+0iTONzhXnXt3AWQmZSSTdvR4f+ze/xIf+yl/DcSQ6F0hpwi8cO59KUqcd87YhYz2wjDnf2OtN63HSk8ViwcSSOY7pONuN9vVcr7n+chynCfx74MeKoujdts78P8j2mwwg7ZV+171eryJ7TlYhlp072RGbdMq8vTuhbyOn2cdMevLY32FsLRRumQxs3w7LF7GETuuuaV1IJ/ErNIaI6Yhy96GUH8QTHlFjYzxSTd0NEApaQbPakWzO3qRBmvkZE31l3w97/geqqtI6K04OpmpXLXS1SNnL4H8+7Xar5ID57HV6PPv8RUYx7NzaIY9izp5epjUV0JhqkmUhU7OLjJTEC1v4jRZxIcm0wPUEw2iHBpI8ivCkoNfvEISCKI4Iawa8VghyR6IwmEyUS0S9Rer4dIalI4UGFUegUuqeT6vRZDiMWTlynEzBmfse4OEHH+G7v/ODaKWZm53nwsXLdAcRh+88ya1Oj7vuvpd6o43OU9ApTqHRGBxmFA9Y3Vhne2uL977rEbJBj04vZZSkaAwBUno+KyvHqmaF1tCPFXuxptZeoDFjrG22t7eqcFBHKxYOLpFogfQloyRGFZp6s8nc/DyeK4n6A+I0xQ+CyvM8VSnPPPMMCwdnWTi0wjvf9yHuf/uj5FowjCK2dzdI8i53HD/J5StrPP/CeW5t7fAHv/cU33juPFr6DLIUN5Dc+6bjqGxQNXPs5qviCLdszuRK8/XrKeev95CFT//GOv/nJ/4dMzNtarWQPE9Jc2W4VrnGKQt2G3Flj3uTygx7AhkbP45zBSZPSrYLbouA13O9poXKcRwPs0j9H0VRfKr89v+rbL/JANLWdAu0ZnqqaQaVA5lW4AqiJEYX2ujeSn8lXIF2QDtm9deFwhFGH1Wg0Zkii1OkI6lJ3/hbOWXsuqfxpUCrFCmoJnGWq1I1rirQMEnSkjQoqkUwSmOS3Ig5A1l6oecKpRW5gAKFI0C45mulIS8woGxh9IXDUQTCfG2rJ3u8nLSqybIUlaeM4ghdKHCMj3zoB7iFQOix/7r9mEyQrtWCSn7zpz8LYHNtHT/wyeoBke/jK5/d7QFu0OLpZ5/jzlMnuHGzgx9o9rIUCkVnp0sSKeJRbLhBUpIOImNh60LOmJqRFxIHTe5oYk8SSEHgGk+nhh8ifZ9RqugOUwYZDAuNlgLhCeI4ws1ihIqoCU02ShGZ4NVr6+xFHfrJDo2mz698/Nc4snIMT/ts7WwSTjW5eXMVhwErh5e5fmOLZqtNMx+wOL/EQAXkqeKf/Lc/wu7mGv0cvJpPPRAUDsSJJs1TesMuNzs9tPSp+YKaZzqXT3z9OXZ3u8wszDM310aplNr0LFu54PEvP03s+DgqpSEDPCRRP2JnZ4c8TZlutahLybDXBUfRmArwfMH+/g6DYQfhKhrNNmff8naW736Ao3ffS28Ycf99Z3B8eOeI2F40AAAgAElEQVQ7HuX6zU2OnjrB1FSLl89fxhEBzdY8Qms8J6JR8ymUxnd9yEEUApQNEBUUpXJgsxdxYaODkyiKjXV+41c+Sq4jskxDJvBcH+n7ePVmWbH7lTuDTS23n/1yHtRrAUWuq8h2lY4zLNGglcGNbRbmn+tC5ZjwrX8DvFwUxc9P/K8/t2w/x3GI47j01qFyE7B40GTXznbY7Io8dsQcH/eEK4wDg1b0+j2sd7StvKwd8SQvyVZsttNmo9QnY45q5Y0QmIAIqyO0l+1a2qMeUPpNjVNujVjTr/4W61o6GUhhS2rLZrZHNdsxmaw27W5lJTxjl0hZvS8W67PlusXslo4dw/MCQkfSzCHPUiw8l+fgDVPe/61naEifohjb01jtYxAEVdkfBAFFqqhLH1lAqx6iooil+XncXNPwxoZ9kzik9f72PGMDvLMf0Rmm1FvzHDq8wlSrTZJr9vZ73Hn8OMkwYqbR5u6jJ5ibXwCMdXKtHjLTXmRudoHFQ0us3HGMbr9HmisGUcTc7DyzC4vc2t1h+cgSz7/wAt1ejyROUUqjMBVIq9Xi4PwCTm4aJ6Nc0U9TsiQmVZq9GH7rC89x8dIOcRrSmj7OxkbMHz27xnZkLIxdxtbBriuYnZ0lqAfs7u5U7OzhKCbPNEWqGe5H/PEf/L6hmXimkn/Xux+lOdXmvgce5pvnLlKrh/zJn3wJ6fm88OJ5MqU4cHCBy1cuM4pjHBcOzM6SpTFS2pQjQ740cexU2GyeKygEu3s9dvcGNJVm6+ILfPyX/wXTDfP8uSpNIZNu6aygqrlo8VmrrrCawSiKsBF0VXYk45TlMR1oHAn3Wq/X8ui3AX8VeNRxnOfKjw/w55ntVxSVlWmtNrYhtphLFbrImGJgJ64tQ63Uxii+jWUGaFqtZnWUs1WKTa21i4elQNiBZUFwe5yqxJxJjCiPelmWUmuEEzjauLqB2zWFFjA3VizqNo7U2DZ43O61+Wd2EZJSVib9kwnKSZJWMWOTPtQWxDSeQFSL08TbbXbJIKQADi4sMB0GeC4EnmRhdh5ZCK6+coEDMy1q0pAf9/d7t0mWrITHYIfQqPn4AtxCo+KYqXpAr9OhFQagxtmLdtBaz/dKmygFhRS4fpP1mx3WNjfZ7Q/QQtIdRvzBF75EszVL1I+IhzFvfsuDnH3wQTp7XXb3ulBIDi4uM394CWo+c/PzLB5aQnqSJNM8+Y1n6CeKf/D3f5yXLlwkilPcWkB9qgkC4ixl2B/Q3euw1YtJc/DqTbwwwBU+jSCkHyluRYIvXtniV7/wDP/qd77Cf3jhMt0sQCmJJwXZxKZkPf+VSmk2Q9IsNolHJZdM5ECiePZrTxG2AiPjCk31/e7Hvos77z7Lu977IYajlEa7yT1nzrC908P1JIPhgIXFReMWMd3EEeA6kKkUR2hynTJpUFCNj/L+J6lmlAturq0z5Wr03gb/+n/5Z8RJjzSLUZlxM7GhDmNt6jjnAKjmi+f5t6Vw2/Fr56fdvG3O4J9rAGlRFF8pisIpiuJMURRny4/PFUWxWxTFtxdFcVdRFI8VRdEpH18URfG3iqK4syiK+4qieObP/CtK+cjkC7PER1tBTPKCJnVHtgth9W/mZuhSWiPI87TCuewiaLEHu0tM8kQmiaEVUa+8wW6ZVOw6Aun7xGlc7Rz22GUXIEsUVWocAWZ/r30Nkwz28b+pAHXLIrf4muVZWcxuMsHZDpjJpkK9HlZvsRlMLkplSOkipUuuHKbn5hjpjBP3383KkUMIpyCNR6AdunqEmKqzcOgAc9Mt6vWgSssJgoDBYFAt5lmWUvckdU/iC2gGPsMkItUpwyQittmFWVotUJOhBLVagNDmdwRSMttqIaQkVopeFJEoxfZwwBPfeAZZD5mem2dqus2gHyH9gExpplsLNFuzuH7AoaMrzC8s4PqSWhhSIIkVPPLOR7h86SJ+LUBpY5u71+2R5KrSvfW7PS7d2iF2Sr+vYYpXb5KMYnxhnECHfYXOpYk4SyVSaWoofDR5uXHYKt5w/xS6GHeghStxEbiOxEk16XDA1u4WjjTW1M2pWRSCe84+zPLxU5y+516Wl5d48dw5Dh5aYf3mBouHl8AV3H36FF5NcvbsGQ4emK94gWZcjnWxlX7PNglcn1s7XQ7dfYpoFBF1usS7XX794/+SXq8DSCj8srs+uI1fdfsaQSVSNvPENnTGDp8VXjZhjfR6rjeGzUtREMcGIHYcg71kcVo5FVCUVqyOEezqkhdlA0ltMICtxOLSo9kpAzbqfkCB4WFpbfhIdqIrZX5XlqXIWkCa6lK8bBaVsBGgC1Vp5oQwi490zbk7UePgUs+TeJTpH1m52DkatEKgKXKF9AQ4xkqjIMcT47BUX7oUokBlSalthNFoVEl+oEDgUOQ5Sud4UpLmeTkwC4oixzgxeNVgEtJ8PUpG1MrXbf2oVpZXePWV80R5zDMXLnPj1gZn7jxJsttBF4p6uIAAdnfW2OoqHGrUhAmlbLXm8SWQC+JRRFinklPU68bx1JfmuDwcxfhByH6/iyMgzSOUVrhOmziOCHwHnBTPMdmESa5QKHytEY6iFTbJUkF3MODla2u8/dHHOLxygmNvavPQQ++oukuiXPSiKOKrX32CQ4diVJqyublJN4dmGLLcaPPc15+l34uJU2PxfGChzU4U4aCZnWty9K4TfObLzzDdnCWOYoRUDLpdpG/0cFmc4gc+SWboFLo8VmflkchDkOuYRi0gGsb0ZIuH33SK1auXibKUejjPKB6gXYHjQi4h1ppnnn6Ktz7yDtACtxhQq5sxfuyu4xQCEiVYHCnWr60SNkOur6/RrAds39xk/vAswcwiV1c3qTWCynZaZ4rMhQxTbbkIHFLyAjJ8dvsRz754gaarWDowy6C/g7uW8nu/8W/54f/yx0gdH6VifC8wFZY0gaR2sUmSFNcTxGmMKyXSF+iJ6jkvAzYQmHvuSQzN8bWb5sEbREJjHT6BKkLKvtG2Yhnn7I1xGls9TDK0XVdW2A6YyiTXBrsqynSMsSXLuByeTICpXDWLSQ7TWCYwKQGwxy7TQRwfxezjrEzGVH5GUKx1jus6t3UmbQRSpSmcYJVb62MpJUEYgDAE02LCG2jMWTH4mpUcjUZxRXWwA2sSJygKzfz8PO12G3K4tbPD9Nw8vX5Est0jutXh+MwiS7VmlYRik4D6/UEVbGkqx4B2u115d9cbTUZJaljimc18kySJMu3qOMXPQSeCPBbgp6RE6EDTTXvEwNzcAiJXNH04MNVGaPjU449z7PRJamFIvRniSIkXBEjfNw2KIODRxx4lbLZQWjMzO093v8PcXJvz589x/vwFfOnTbrUQCIb9AWoQ4ypIhjEvvXgBgF7PNLfN4htUlcFkuO1klW/TsotCU5PGu6pwJXv9ATd3d2hNtwm8gCw1icl2rLuuxBOS3a0dbE6lTTlqNs37fvqeU7z1bW/nTffcyyNvezvHTpzkoUfezsz8PFfW1tnvaf7nj/475lYWq3tiYZG6A75OEXmMq2M8KfDcMr3Y8bm2n1JbOEYRthBSIIsYJ+vxuc98guaUX22stZqJgbenEovp2fljK2uLQVo+1WTikz1pOI54XYZUb4iFysGZALNvD+Ycez2pSmBsyZuG6Dn2VTdldlThH2O6QlpWXMY+18pPbGlqW/xWBDz5/JNymMnv28sCjRajsu1Xywkzi6xRmdsjndVU2YVyMtbIYlgwlu9MYl1ZbugGGuNQOYmJTT6nBdDt0dAuzFYNbxfE2dlZtNYsLS0RBk1u7XZ45tw5EJJOoVi5914OnTxOX0WVb3eWpbTbber1oMKXwjCk3+8RRVHlFebVAlwvIGy0SJWi5oeV57orfDp5RN6Q9JIeyklxY1B9hUh8fJoUQtHvRczNzFPzBOQaqTSdnR1+85OfpNlqkipVLdyOa9jPGojimPe+/wOEzRZhs8kw6vDtj72DXr9rmim1gF63h84UjTDk0MF5lhYX+a4PfpDckWXTwK8mlp2E1vIZqAS89n2398J831T4uYDMgdWbO/RiAwvIUt5jrKSNDs5Rmu3NTYMnlhujxWMBalJy6PAy9775YXIR0JqZ5+KrV7hyfRO/3mR7J2akJFkWc2A6QKgIHfc4cnCWsFAcCAPe9dAZTq/Mc6AVINMBNW3Iz0MleeLcZb7w9Hkac4sgfJL9DjdefoHP/MbHaLWaJf4bo9QYnlDKzEnjrzZJIha3jWMrc7PzTCkD5VQv7jVcbxiblw+87z1onWMpWRZ4E8LBaAHHyb9Sunieh7FLcSlKCldRFFW0lRAGjzFdiBwQ6NxBCJeC/DY2fG6YfbiuO7GAmdXeEQ5G2Jth46iKoig7Gk75O8zzK5VVlZNShgptFhCn/NvLtAWKMe5VLn6j0YggqONK8/ssLmf9pqQ0rzcrNK7nghAonVPkOdYOxyrXDYHV2N/ooqg2rnzi7zNXQXdvl7XVV+l199nb2WeUpmhHM+XXacoaN69eId7v0t+LSP0AT3rEUUQ8inGcnGajRZrENMIajoZms0mn0yEIauwPR8RJhvR9srwonVSL8rhU4KUpx5sNHlo6wPd+ywPcf98BTh2bI+puUHMiXFeTpi69Ycbufp/6dB2ZF0gcvvHMM9x13xmOHF1hOIoQ0qUfDVBa4bgO0veIY8XJu07w0ksv4Tgx+90OKklRcUqeJTSCkDTLSJOMfp7S3e9w4ZVLbA4ikiw1nlQqNyTINCOoh+Q6N06rFCRJgnS9cvPJSxWFR5ZloMALQqIkA2ksVRydEkoPX9aIVFJBBlrniDyn1mzw7ve8j1QpBE61aTqOg6CgoKA51ebo8eN0Ojs0p1o0mlMcPXaMz3zu88RJzsnFWU4utviu73gnTa9g5eAMeVrQrnuoqMdCu8Hi3BQHGpKw5tIbxsgCCmqo3GVrc4fcqbF8YBY/T+nudnhlY52HH/pWksQEOeDY8AenGndjaySgKEjTrDxpOAjXRQin0tXmykE4Lp/+9Gf48Ic//BfH5sU4aIlytZZl52u8UyVKUQjDmcoxEzGKIkajyDyuEIS1EOlIXCSm6Wf0glJAmgAYbEh6t9usOBIKYbgzmU7JtHmuHGW+P4pxtNnRfNcwb63yHIyo034EgY3CyhECHEeDa/5+1/fRDuRKlxPVVBamIhA0W+aYkiaKQhs5D4VLEucU2iVNchw8KBzyrECnOX7hUggXx3XJshwXF6UL6n4diQtJTpEXOIUD2npZuTiOiyh/TtbqHDh4mLzI8Zo1gpqHpwo812FITjBd49T9dzOqBRSJIh0azlSr1SLwTBU1imOiBLqjmML3cYKATAiyXFALQoaDCJ0p/Joglz7C0RwKNX/nh97H/XfPMTWluLl3mUsXLzAVCN750D18+K99D+95y738le94mJYXUauZ6mpnpEhyzYGpJj/9U/81v/WJX2NhdpYi1fiuMNw2JEILppohgR8Q1pocPnicaD/GzTXtmVmUEHSHKVOteRqNkMPNNu99x7s4dc+9eF6TZtA2Q1NoY7Ln+USxSeEm93GKlEYQ4OGTjQYUSIT0yQuF8CAo05I8oalLiJUmK3wWlldApDiUEIGQuDWfQgbkjmYw6OGWjhJW7mVPFHmuyIuUesPn3gfO4gQwd2iRly+vM4xjfviDj3JkoUXU7fK1J55gc3OTS1cuk+QRiYLBSHJlvcfq1hZJrDnabPN973iQA57CISaTgoGSXFrd4Mnzl8lbLaYaIZ1L53niiS+gfF1V63YOWUcTc5IQlR62MdXED3yEHLvRWrqC55uMhNfDEX9jVFS/8Asf+cD7H8P3jQGeqai8Su+myyrA4AEOUrhlB88teUpuRS3Q2lRHxp3AJc9zgqCO1jnGvCutKiApXTTm+Ob7XvV9KKpSvtlokGVZed4vKsmO0TflVeu/Xq+jtZq4IYXZdVynsrgoyjiZJEmq3VIIF60LlLK+WxIh3PLD+MUXaHSh0UVO4QBFgQPl7/XxhUQlGZ70yIVjgirNC8TQNMYDwh5hhRBkKsOTgu1bm7x66SKzQWCSgfOMuVYTqQX7nR22d3a4sTUkdxymp9tonTMajUjjiDCso3VBqzVFgcN+r88ojgnDBpHOQQpSrahPNRn1RuRA08t437sfZK7uEDYE9WZAP9lnNIhxhaDb2UOlGTdvXeOuO44x5UvuODjP1sYmnvTI8Oj0Eubb03zz+Re4cn2Vb//Aexl0h6YCFwWuW5AqDbrAlYJXL11gb2cLoRWDwYiiUCVOlJAkEWncY5SM+PpzLzFQCU4hyuq9vO9egJTgeR5aQy6c0ol1SM3VuEWG6xbguGS5Q56leJ5bdTpVllOQo5IRHprCdYxts3BBFGSZwgskp++7n3oQlpCAW9JRclSelx0lg+14nuToynH293tmIcgjJIpo2KVeC0lVRmtmmtEoot2eJRqOKHAoHEVWZOhM4QjJVmeXIwvTbO/uoYREERhBt+Ny4fIa23t9cFyubW7y5rc8iCMEge9TFEXJa/RKmVdxG91ACAel8nK8lacTx0IdJg7uM5/5nb9YFVVBgdaqbOeKCXLjJFgtqhXbgtsWALdyEQuAWwKibfdbtrn1MR/bsuiKFjAcRtWR2ergLFWiyujDWgH7FSZmPaiiaHhb29b8fWPSpcFyHMDB92s4jlPxmWzb15Be08oLSCmFRhGnMV5NMj3TYnl5iePHjzHVCmm1mkwttaElmTu6QHt5lsW5NocW2vg+1ENZBT+MDc90dRR0XSNp2N7e4sCBeW6srzM91WZxaYlufwBKM4gUb3vsu3DL/MB+v1ct1PV6WDUYOp0OgyhieqZdgeiFUnhCQq7J05Sg6dNuhTTrPlMeeEHBzZsbdLtdRCE5eGCBwA9wCtjf6xL1eqzduMLWzVXefPdxHrhrieW2pBj1CAKfvd2YdKS4euki/8PP/BTzBwyQnysotKTA6OkWl5dLH/CxHMuQFTWNZkBQ9xFodvcH5J5ESr+6b9ZWaDRKK05UkkbUZYCrY/7So2f5gfc9wo9+7zt402ITT0X4aIK6AbJHoxSdm6ZIIjSN0tix7gegIRpGZHEZkx6n3Fhdw53AHbOSye1KH+kFuNIHRzIzN8vM7AJvf8e7mJmf5dTdJzl3/gJXrq6ROZKd3oCd3Q5B4DNVD6h5ktFogOsbuoQSgpv7XTq9HnNTLd586jhBnprYtEzR6SsUTTojzbWbHa5eXeW3H/9Naq3gttg4y0u0UIbFhS12OSmhMfNCV3P29RjnvSHoCWb+jj2nPOmXosbSAcAda/RcVxCP4rIrONYeWbDZRJanwJhlLgSV8NcsLFT8Dl1ysuziY0tbi/OME2nGhmJmck6ajNmKS1d8kklWvZRemZpjtGxgFg4Lzv5prZ7jwOzsLEUBl9Yu8tRTT/Hkk0+ZhsIw4uDCApsbm9x1/Dj72xvcdfw4Ra7Z3t6h2Wri+j7XN9Y5sLjIWx56FydOnGBhYYFaLSBJ4nHqjStoNEKOHj3G8ztb+PUWa+ubBoPIFEtNyeKRY/z6Jx9nkBt3iHq9WZFnnTytBt3Bgwvs9SNubXcAY8ZWd6EVhOg4xUUwiDsoHTI35fPOb/s2PvP5TyH9kOX5Ra6vrqG8lNFwRFDzWTmyjOtoXN/Ha4RcuHoRrQd83196lI9+/FNkvkblAVO1gO31TQadDj/zj3+Sv/tjP4FKfdIYcs/EsLfbbXzfZ3Z2lt7WJtPTLbq9LYRjNrpmM+TOu0/z2T9+giKcBYwY3S3fH8/zSSIT4VYUmmYzIIsGnFyZ5V0PnmT72ip5ssPJQ008Kbm40aOfxGV1PNaQFkKwen2N+w4tsjccUCiM7MRRKCAZRLx6+TLveM/76O91q01SKUUBpJV3mmA4ikkijVMoGlNNrq+uE9SaeEETXEmSKU4cX2F/f4uDB+bZuGF0iRmKgwtLDAYDCg88R3Brt4uo+QSOYqRLyAVTDCQqhSIg7g946dxzvPjSc9x79HTV5KnkUuX8sGPedsYnfePs5m0JpK/nekMsVCCoBe2qgspybTgq0kdI30SSl2x0pRSy5pNTBkAI0+lJktLGxDVt+0Ip0JrA8+mncSn3MG1zTxo7FyF9cp1Wq77nSeq2q1MKLp2Sb5XnZncLAn9iQZG4jmewn1IvWIBxbSxM5p7wPTKlkZ5PkiqMFbu5eXaiGElFxB3HTtAZbvDrv/Yxnn3WaMqOHlmks9PBcyX9fVPWn/vKE3zg7d+KG3W59/7TvHz+BZpBixaC/bUrNGdnqWtN78YGX9z6FM9MN9npbKFzyf1vfoCH3vatnL7vDLdubqFSWDiwRJLFdOMdXAQtv4UKUkYqxlMDDsy06O53oW5M5+I4ptVqEfUGzMzMopRiY2MLHFhYWGB/v4fvSjrFgGx/B5RgujVLDAg/YG1rgxdfuUAgBLkfIqZCgpmAxlSbtavXyCLF+vo6+/2I+4+foO5IZmZmWb++ymc//Ukeuec0vRF8+fwF8tosMjCODRsXVvm7/9V/xj/8h/+Yw0eO04s1cZLSaIQcOn2KL/7JH/K+hx/k6998jnq9SRRF1GqCMGjywtpl8kCQuzGFUji1kL3uAN8VjAYDGlMtut0utZpPf9DlxIFF/uZf/cusrz5Lc36W3rrm7JuOEfirpL0u1yJNUpiju04jcs/HSSUFgpEG1zHJ1K4vSD1BMVIEQcj1qxd59ZXnmJ1bwPespVFMeeLHlT69XpewbnItswy8oM3c7Dy97gZNGbK4sMDWzfWqktnubjJ/sM36zQ5ODr1ur+qga1ewl8ScONjm6NIs++tdtJbGm10J6qHBFE+32xSdHn/yu5/n/p84C0ONTjWJimnUmyWWNg4TmaTs2E6pcc5V5OXJxnkd6Q5vDIzqF3/xI+9/33vLdnxOlmUEQa06B1sSa57n5Hletr/zik6gMkWtVqva/GACGSgKcqXAdcrHO+aYiFNyOpwKE4OyTM3zsiJykNIry1q37D4aIXQBqDynAFwcVK4oKPBrPrnOcXDIlSFk5iW+ZuyWnUqyo3VhmMq+ZGZumief/DI/909/hk/86q+ytX6TYXef2akpbq3uMujH9AYxjgxQ/RE/+WP/BTu3VpmdrbN5/TrthkdvZxvPKZhpNqlLF4+c6UaN5QPzjPY6hJ4gqDlsrl/h5Ref5Xc/+9u8dO45DszNcefxE7zy8iX21neZnpojjobUfE1aZDz63vfy1NefYZQmgCSsNyg01PwAyI0Vb5rRbrfJKUhVRjQaIVyBKnIOLR4iiRJ6+z1UkVOreRRqRBiGNGsOM3PG/UAVDmfPniGOYw4ePMjubgcpXa5fX2c4HHL48BJxPOLw0jLH7ryLtRvXWF5cYPvWNpkSeMEUcTQiqAf83u99llrN4U1n3kySKnpRSrNRY39nixtr10hGMcKDoyvLuI7Dw297hKeefZEcQVE4+I5iVmruO7mCzjLSwsGGfZjKWzFVczl11yLr1y6Spin7cY/V66scXV7k1B3HaIQhr66tk+Lg1DxjTeT6+J5kuhniu8YtdhiPSCkMbpMkREnM6toq73n/+yl02fET4DjS4H6jGFd6JkCknB9ZlnLxwisMevsMRzGrl64yNTVFs+nRbofsdXrkqsBxTEpTnKYVbtRut8mzDBdN2JxiY2cfLTzAYGPCzTnYcLjvjgWm6g7hVJ3uKObo8lF04VAPfPK8qDrJUroVZjw++pn5ZfFjv+xk//bjn/6LFe7gOE51XLK+5VbUaYFfeyyDcfz7pF+67Y5YbGkSj5FS0miEpaFdeBtHy/f9Em8Zh4hOag0n7WQAhOujC4EuHOMrNeHtbmUhWhfl67LG+VElobHWGNKD5SMLfPK3Psb/Rd2bB0dynmeev/zyqKysrAMFoHA0Gn0SbDZbzRZJURQl27RkHd6VNbEe2+uZ9dqO9azXY3s0GtnhcUzE7mo9M2F71uNrbHnWs74mfMgeWyNrJEqWKIniyGTzbjabTfRBNIBGo4FCoVDIysrK48sv94+sLDQduzYV4diwMgIRYKFQVUh2vvl97/s8z+8f/6Mf5P/65V8m6QUkA4mKwOv4RH2JEBa6ZiGHEhXDTL1B6LV55yP3IzXFkcV5XNviLadP0ZpsUK/YnDl9kpotuG/pKCLqYamAGdfBjnxOH5rFSWPKSUiwvcGf/Na/58d+4Pu4cvFFlIKtTge94jIxOcvRe87wn/78MbpeniwhxEHueRTFxFJi2jYlx84hCaMECk3Pw/osy+L27U2SJObo0aOUTCtX1WPwxLOXCcwGi0ePMzM9xdLS3Xz1ySeJZczSPaeoNWpUqzVKpVz0uLy8jOf12O95dDpb1CcsHn34LEuzLnU9JvU9Uk0y8GMc2+GJLz3OFz/3KVrTDZSKmZqYolRxCZXk7Ln7sWyb3Z0upmHzqcceJ0oEKsuTBSbKNt/y9jO8/dQirhYg4nCcxR+GeXzPW86eYt/r8uCDDzI/t8D8ocWxO2FjYwVXhEw6BiWjWN3ndp0wlWy023kihw61uksSK/p9D9dxsTUDb6fLSy9dGPcv4zhGZeD1faySTcYBDKQwitfqLpVKHtjYbNbY3d1isjmFphmkSYzveQSehxxNEnOeQO4gyBAoqYiDgJIADWP02gYZklbNRsmASgkyr8vaa5eoOBaZpu4YAolxT7YYKBUtqEIrWAiDD7aN32CCz+K4sxek63lSZqESL7Ka7gzjypvuByem6C0B4yjgwkMYx7mkIYrCN6i+i2VqFMXjE3tnISuKXVGQUqVQGaAJEpkrcIvPkRczbZx+UPxN5XL+d5TLNmSC2dkWTz39JB/+pz/KXzz2CfzdLcwY/NseYSKIpCDFYRDCfuSh64qSgGS/R7VicPPaMs/+5VPUG1Osrq2zcPQ43f0egyDGCz2Wly+hiZhrVy8Sxx73nl4iDn3eduYs0g8Ydnrcs3icQ0bcmV4AACAASURBVNUaM67NI+dO8aH3PQKmQnMEe3FImAq+8uR52t0YTbhYJZc4loRh3v8TwsBxHaIkZq/Xw7AMlAZGycK0LfxhLh+ZmZnFsixu3dpEygS3ZGGYDqpU448++wQvXVom8rvoMuBtDz/EW86e5bOfe4xe32NxcXF8bl3X5dix4xw+fJQ4CbHLgiDocPeRJh/7yR9G7XsYdg6sSIYK23D49B//Ph/9sR/m2OG8Sd9oTuE0Grz88kVMy2ZyYpbQl+z2QmIl0E0L27KYn26ytrrBoNfh6LTLsZna2FRebG1efOlZOp02cRxy/fp1bq1sUXenWLu5iTvR5O67FnngvtMQxhgSZKIwLBuhWyQZ1OtN0jTG83pYlk2plItndSHo7/V45ZWLTE42RzYxmxQwSjaRlIg7PKljM3sS4pjQcCyqdUG94XDt2vU8tkcTTE9O0Ww2x66OLFP4vj8SXubXQ7PRYGE2T6SI44IuI6lWXBIpyTSolV0cU3D+/FNkoxy2wt9aDJbuzJoqalEucC4M6QUf4c030//OFKqiKluWlUe0pEUSgo1m5Hv7quMi0hG5JMuja2VyoNq+cxrn1muIkkGEQlcjuYJpIUwjJ5wgyVJJJiWZVBiaQAdKjoPSwB8GZAIMw0KpHP9tGBaaUlSdMmXLwhSCUMYoRvYalaGleSqiZeWE3kiF6CYIPQd9Ts26fPQjH+YTv/f77KxuEXUl+wPBXkLeIFa5q912LLxBm+lqIy8OtkNqO6wFIX/4lYu0jt7PyvIWk5Mt/vKp8wQR7HoBw8gi0VwGsU2sNZDSYvnyVaZqDa5dvkjsd/kH3/1Bon6Hod9mYcZFDrpsX1/lweMtHjo+Sy0L2GpvUq3WsC1Q+COAhCLLJKaZG7+HfS/XAQmHEiB8nyNNl7qImTQNGgJ2VldQQ5/ZyWp+Yfoxw0GAlnqUzRqf/MKz3IrqnL/e5unzL3J5+ToP3P8gh48scuXVyziOOyqGXaKBz3Z3nW5nk4pp8cJLl/D3fb7y2Kd4zzvn+ftvP4dtB2gl2Lrd4+TsAuXQ47u/8wPsdDc5ffoM/T2JYVlEg5hzDz/MyzdW80kxec783SePUhaSzU7AK7faVGZb/MD/8CG++5FTlDWF5TZAE2wPBZPzJ3ErU5xZup/Z2dz7aBsGWRSzdmOduVLMTEWhdIluWhipQA4kKhFcWF+nPtGkpAtU4pEqCDNIhUWp1ODW+grbO+1cVxfnufJ6JtHS/MscxQVpCkzNoDLZhJLAqebJC63ZeWZaLZq1Go16jXZ7iyQKOLI4i2VKahULkcbYI72gCjzKxAyDHtqop2qYgkbFZrpRI00hCHJR7NalC7x26QJGxcYwDoIkixu6ruUoucLAL+N4TPvWslx7GEYB33g6qn/3qx/7ju/44IF/StfG0wQp895SmkjUKFUhHacDlEmSZLzkLLDl8QhwWKyyhAaGlSu7jdHW0jTMcSuvSCmQMkUY+hjYCTka68Cvl0NP0zQdva85UpEXSnd9vLobDAbouo6lcjFmpVpjbXWV/+0nfgq/0yULI+K+RzCUCGEShRFlp4puGgz8IYcPL7Dv7VEq28SJpGSXkTJlMPBoNKd4/KtPExlloiTFdMp53LBuE8qMfhASRZJhHCHTFIVG3w+w6lVSNFZfX6ViWdRrNn6vy4kjRzBFxubNFRYXWtQciwfvO4M3HFK2TAaDAWgCS+hoIwJmEkWUSy5WBlUhedfZe5idMPmH3/lBvv1bv5kHziwxHO7yEx/5X2g1y+jZAE0OsTSNMJTMTB+h5w8J45SNWzvcbnusbO3R9mI+8elnuN3tYVkZzaaLWylhuxWSTCdKMo6dWGJtbQPbyJg7NMv2zm0WDs0S9j3mLQdvENNVEt3SKZdLyH6fv/jiF3nw7H2sXLvOTKtFkg547epVwkQyjIekSsMQYGUJaTQgS6Ck65gqZX93F6UU19ZuoxkG4TBCEbNx8zoVW8MtG7R3dojjaMz5g4y52Rat2QWurmwghUAlCsM0QMvQTZ256SniOEQIi0zTyFJFKlUO3LUySk6FxcUjeW8TRjo+Exjl44chlpnbxpYvv4KKA7xul35fomUaU8067fZG7ljQcxN1pmX0egMEArdcxq2UOTRTQlcxMrO4fmsPL81y07yS6Epy15FZkHlWmZbpTExMkBg6d997BoEx6h3LsZ1NFxo5qzMld2Ho489cTP/QMj7zmcf4J//kw99YPSpgvOWDAz2GbduI4g/UgNFWLde2BFRGmVDFVqtYohc9ISkl6UjaQKqwxrKBAzkEMJoA5d4r13XHuq3cynIQq1IYfovPl7+GmRuG7YP+WIGnyqSi2Zrik5/8E37jF/8N/a0txNBnqmzwgW96kG996Czf/q4zfOi9DzEcdsaaqq2tLaSU+HFIMs7zMTANGwwbo1ojFBbPvrbChWubLN/q4eHSDWNS02avHxBFgm4/ZBDnE6iVtS32/ZgwlnS7PfY6PaYmptjcWGd/r80Hvu2bKQmJSDzi3ibWoMuMY3BidorpikM4DLBMAzJJxbHRpOL43BRnTszT6WwweaTFbujz8d/7v/nahacY7HW58OzTXL38IscOT/H2e5f48R/6Lk7OO/S3VhgO95iZaWHYNn6k2IsEqzsB9kSNnUDj5r6iWmuys9tjMIwJhjFZCo5lU9ItqqUKcZIwOzPHdGOSVA5ZnK7xHY8+jBH02N3rcmOzw3sefR+TtsXvfPzjBF6Pft+jPwzxByGJhHKlhmFZlAzBXccXMcsujuuQKYMoFnR7PRoNh3/xEz/CbN2hUnZoTS/gJ4LNbsDisaMcO3kcw7KoVGv0Bz5RErK5sYrf3aKCRKgYXc/Ta6NUkqSwcmsLtz5F1a7hlh2qFTePs5aSqB/yhb/4DHHi5z5G7SCdovC6FqN/07QwDYs4lkxMTFFvNCi7LldfX0FhUKm6RDKm3mgwjEJqbgNNQdm283SPNGbxrlOs7wXspwLdAmHmOxhNwjAK2e122bq1SRSG7G636W9v8NLTT5K7Mw7iv9UoyaTIQSuijwq91YGZ/+urEX8nChXZQWO7SCEsFNRRFJKlCmt0EnTzIImzEGoWW7/C3Q+MT5ZhGMhR893UDZQ8MBffafjVR7ExaZrTlXNne940Lr6XMhn1tuyx2bdQkAshxiPfg+Z6TGOuyb/+Nz/DJz/5h2hBj6qVI58++uM/yNlTRzlzYop7FlyOzthMN2Aw8Mefy7JyOKU+6svV3RpKQej3mKw7DPfb3HX3acLMQp9Y4OW1DrGw2NzuUptosbvvg+XQ2Q/YaHcR0mBns0sUK8JMMQgVqzfbpJrBxPQs12+soGnw9ocfJIl8KkmME4dUMyhnikPzs2goMqUYBj7zMw1KpuQ7v/tD2M0Grz5zkfOPP0tNa9Jd32WvO+Dyq9eJQokayTteeO4JHjzT4jd/+Z+jZ4pBfwsZecy2mhjCIAnDPOYiVVxZ7XL3qbMcO36K5vQiKg6YqNhcevF5wl6PiukiE0EYKdJEYLgOjZNzdLau8MG3niQd+EQYfP6J81QEzEzUkGHM3l6PaAiDQJEkBmGYp0zUXIdet8MwDOnHIZ19j14Qsj/0GQx8Lr70LL3dNlEQstsN6e0rpueO8+xzF7l0+TKn7j3NzVsb6KaFU3NwKw5lHb7l7fcjk5hM5YUm0wTBMGav7yN0i8gPkbFEALWqi+u4hEGA1+vwsz/3r7hyZZlmszk24RcJHYUrIu+1WtTrDQZ+gFOzCEKfMJLEseB2u03ZcfCHuYdRU2AIg1rFxXUcDMvlhVdXuXq7Qx9FkuUymySWCAz6YUBtokGr1cJ1HKYaNSarLjtb6xjWwcLhzpDGO/Omir5yIWq+E2z6Zo83E0Vsa5r2rKZpL48ApP/H6PFjmqY9MwKN/rGmadbo8dLov6+Pfn70zXyQAqJQkI3z1xr1rEoWmZZnMqtYjhTGecEoFMdFKkCWKVR6ALnMFNiGzTDI0xwz3UDLBIZukWagWwaaYWCUrLwRPNpGFuZm23HyHpRpoZsWlYrzBjFbkQRa3OVSTSGUxNQEi8eP8ou/9gtceekiU5lFtNvmw//T9yCMkEvXL/Hk+a/x2vJVXn75KqawOHtqiWpJEkoP3bZQWJRTgciMnA+YeMw3LWzfo2EKhB6ztb1JqsP65gqmGbK8tsVOKHjm6gq+bbE9UFCuMVGdItYl95w5RRaDtxviVmpohkPXi7l+ZYWBHzLc97h4/jwT1RrvePh+0qSHkfbIel3qQ5+6JZhqNSg5DsdOHmfq0AK/8hu/yevLq8wuLuGrFC/YY6JiMT/ZJJMBmg7Pv3iZW7c2yRILXVX47Kc+w/f/N+f47X/7M9hZzK3VVbLIp1q2MTBIYiiVbf7gzz5JEPWolPNcKtM2qE1Nce/9D7EXdEm8NkJKrm9sMuXW2NvpYFcdls6eICVDaRaDDFb9kKzWJNFARgGDYUCcBOhmjEoVmcgji4d+jJnaJFGAXauRmhZBKHj9xib9nsfibItG3aWih4iSwUuvXWV3N6frdDodIB/5t3e67MeKZqvJwqTg3GIDS5fopiDTBEIoYmFx6fV1anNNXCukP/AJ4hi7bFCxHKq6gRGH/Nov/Ct++n//CKEImWo1qZVdNAtCGVN285RWt1bLBceWy9Vr6+z1Yhy3RRSFTM8dJwxi0iAAqYjTDgjFVqdLRIzlNrl4q0Mvs7AQiMRASxSRlOwrwe29EJUaRMOQIPTx+h3CXo/bq5v4IxO/UnIcilisoopFhJ5KTA2iOESR8ziNPCHwb69QARHw7izL7gPOAR8YZaH/PPBLWZadBPaAHxo9/4eAvdHjvzR63t94FM7/N6q6D0jHxSootyUE498rKvMBlqcYhR5QWbNMUa2645TCYmJSjH6L18lFoweTiSLGI1edp+OtXxETk6YZw2E43mbquoGhCbwwgIrgJ//Xn+Lq88+j93tMmoIf+v7v4w/++BPMLczzwgsvMj87y7u/9ZtpTjTo73WZqtWoOQ4lXeTCUBlioHBNRVkoDk26zFQdFg+1aFYs5hs1ZN/HygRJItjuhWiGRc8LEKbLzc0ut3c9Lq5t8vzmFpe3e3z54lWWuz7LewGffWmVJ5Y3ubzpI+ot4syiN5AYJYfNrTa9fof3vf9RTt+7yKE5h1pJMaFJ6mnMQtVm5+YqvXaH4TCiPjnB1t42T794mZuex9p+wNm3nqNadbGAqZpDmsFWe4cba5tst3vohssn/uD3ecd9S/zSxz7MD/5334aZeGhpQKZiholk+VaXuRP388KrqzjNJrsjMIJtK1AOutWg0WwwM1mjZAqOHTlOqzXP7NwicSrQSHN4phLcWFtnEEkmZlpULEHTtRFajDBDSqbB4sI8C4fnUShKJYder8fubg8QaNjYls1blhapGQF+GKMyi5dfXeHijU3uO3c/q2vrlByHYRTTbDaZmZllZmYWKSXf8uBpjkzaEPmQKgZxkBvWBQwymGq0qJUEaewRRD1026DhuGihRA0lrz2/zEd+7MN8/Dd/jb3hFnXHxRCC4TBG022GMsCuNcF0aNRcTKEQaUxtokl3r0cYSUolh1gqUmFg11wSTeE0ajlpJlVYhoVpHEyxi3bH2k6b0mSNoycWOTrXZG5untbMFE7ZorO5Mb6Git1IsUMplaw8PCCKyTTQlMIaNelzc/+b3/9pX8+IUNM0B/ga8I+BzwKzWZZJTdPeAXwsy7L3a5r2F6Pvn9Y0zSBn/k1nf80b3X33UvYbH//VEdjAyqkyCJRiZIOJx96/IjK42AsXW7A7w7qKTPKit1RAFDRNMBwGOI5zIDtID5TpxZK1KJCGYSA0Y1TQojcsZ/PXzU90kQaq6waxjGlMN/jlX/kFvvK5x6hn8O3vfIhD0w2ev3SB977n3Vy+dpVGxcVQsN3eglDiByHVySZffmmZbS9menaBOAjRLIft7S0evu8U6aDLnhcTDvJ/5FmmaNaauG6N5dV1QhTDDGQEWqqwRiw6vTLyPSYSYolbdkgF9AcBsczpPyLyuHe+iaVJstBjamoKUxfMtBqUbUGj1uTa6xuEQcitrV0GqQTNZihBGRYDGXDyxFFSw+Hly9fp73dpuTbzs02+7+9/iD/8vT8iQWAIi6mJJvt7HpZrIISiUrbZ3+9xeLbJ1PQ8S6fP8qef/HPWO23afj4xA40Tiy30KObRR87R3V6l7BicWDrNtauvUrYMIq3Fpz//OH6kUMIgloKJqSm2223mZmfodLoYuoWmxxhRzJmzZ1leXibLFINIsjAzxXTVZnNzC7fSoNPzsC2LqapF1/M5PN2g6lisbWxx8XZAc7pJr9ejWlZ85Ac+yNqNVaoVl2HfJ4l9Zlst9jpddE3QaDa4vd1lLxI8fXEFL1Okuo0QBrYGddflradPcntznUEQ4jg2U66LNGxubLXRyw5e5FGvusxPT7G72+Yj/+ynuOvuM+x2PT73559mumrz/NNfo7uzweHJKZKBj1Gd4qVLF5ibahF4HqkGbsXNI7pNmJpuEPRCnrmygSw1QMZk+gGIQSlFpsFcAx45vUDdgFhBDOwriwfe+0EeeODRN+oc5cFNXtNETmOKYkw9h8ummiJN4Ud//KNcuXr1TVWrN2Wh0fJ8kBeAk8CvA68DvSzLCsFEARmFOwCkoyK2D0wCnb/ymj9Mjnyn1ZoeX+jDYYBdttBHwMK8MOXUkyLnvCCqFCKy3AlvjU2ShcwB8uIzHIZjsELRgCx6XMVXQVRxHGfcHDRNa9RbScakmCIULMu08XvlBTLvL7mNGk8++SRPPv44xydb6EKxtnqd3q7B9l6HW7c32dvr0u/20KKYIPKpCJvOVpuZuVl63R7HTixxc7NDHAQY9VlSzaDd9Rh2NtGMGpqRb0GDQUCw34OBz4wjmD2ywJOvXMdM87Fw5HtUajb7fZ96pYYoWWRZjLfnYbkOJQM03SCIYizNYn8QMlN36A1CTMPD1C3icIhtKfbsXZyJGppKOHvvErf7HjIS7PUVA6Xo7fk4hsXLV64zHIbopo2Pxa1Oj//wu3+ELmFypsmgH7C2sUlJtzEcwTCM6QcxTrVGhuLihWfx93aYcwX/7Xs/yM/+7mfYiwSZ0ri00sWIQ9rtL3B4rsZHP/qT/I//6Ke56/AUE67NXy6/gF6poJVq+GFM0xZ0u90RI3EIWh5KF6cSJQwuX72OiiUNxyXNQkpGbmavTzTZ73pkatQTigTKNNjc6/HQwlkeOXqaByzFJz/1OLZp8KEPvQ/Lsjl27CT73S6hbnDvvad59ZVL1N0aWgYbW23cksGhwwvousXTV9YZSoFh2dhC4OsOV1bWOTYzxWzLQUYeZd1gdbdLkCnSQRfHdZFBzO7NNotLTX7+53+Gb3r0fXzPf/8PqZYafPnzn8G18zZEKkOQklu3NmjNzKISRX8QopcMKrHC73s0Jhx6O13e++738cLrf0qs8uxuxQG9W9cN0sjA83zaex5nH3mQStnBKNm8cnOLjfVVHniA8fUJOQikuFaSJKZUtkZAB4VmCoSep4V+HQuqN9dMz7IszbLsHDmj7yHg1Jt/i//P1xxz/Rr1xtjYaFm5AK1Q3iZJiAJiKTGsEcNeHKyuskxhWxaWYYBSeR9LHdBrlFKYliCRIaYlsEoG5YqNboCpC0qGhSkMRAZlyxpv6woHOJpCNwAtJ3JoIs+0SlWMVSoSCQoBqk2YhvzB7/w2ehBye2ODt929yKl7lugHkvtPn+OFF17ERDA90aTT62HrDrfaXWJhcf65ixyaa7Hd6bE7iFGVBrv7XSzLwBKKetnGJObk7BRTjkFr0iEiZK2zSZIqttfbvOuukxxuuQylT6LDIIgxhSKKAvb7HnthgFY2SNKYBANDF5jEyCRgpdPl6NIJFg/NoZtldC1jt9PFNF26PY/2ZodISvyghx75pCokHXao6JKSZvC1ly+x5fmkCpxSGaROdz/jdi/g8F0LdHs+wSBkZqZF2bG4trqBygzIDHptj9u7khiH3UFAeaLBiy+d5x984CHuP9nCNnJ2oma7bEUGL655fP8/+xhJ2eK1bsDTmx6lRiMfhQ97uFlAARlQaY7x0pKQiYqNFsaYroMXBsSGwW6SQxuiSKLpNmkomZxoMtlwMExBP1W4GaBbXL65yubaZdx+m2EYE6I4dfQkO7e2uPLqRQaDDqWypB/k59eXihNvOct9953FqtTY3FynZgU8cNc8JHmvrDMMGAw8bvshz6+s8uraCt1I8up2h71hDJHEwiFoB2SZxVYQ8/LFDU7dtcTG65f597/0c/QHbQZDj9tbbSIpWL3ZRugOMsxbGv1hgFuv4dgOsRbntGhhoxLJqxdfJFOSNJNgCopU0/w6VVhWTD8TnL/e5U+/+Dx//qWn+PITX+Pqa1cZZIJMFzByh2jZgbMD8u9lrChZNpppMUwVSZpLIb+e4+t6epZlPeArwDuAxmhrB2+EjI4BpKOf14Hdv/aFNcaj/SKqt1j5lEr2HSkDjGNu71ShR1E43goWwIfiOcV2rlqtjacmmibo9Xpv4JwVk4hi+1lMEIs42CIu5c5R7AHCC1QmqdZsfu0Xf4F+p8vc5CzvevB+nnzySfp9n3q9wd13LzEz3eLI4UVWV1Y4efw4a1tbaCULP5CYVo2BFGzvejhll2gQomsC3/cxLAujbBNlEk/GvHjlOpv7Id/2nkcplSwGoU/P9/AHHhN1l+OHWkxVLHRL4Ach6GBoMU4p7w8oDVQWo2kKDdCVRlUvc215nU4vYGJiijixcCotunsemVAkkSRLIY5igoGPngQsTNfob21wZKJGObOplevYlQqDOCCKJFbZplFvsnajTbXkYDo2t3ba9KOAdz3wEP19j0QpEgO6XZ/uXsCtWx5XrmyytHSW03ct4W2vU7cUmoyRo38LhmWTpTa6cBDCIgolSRyjEolAxxAG0cCnYtm4tkVnZwvL1EniEKGBpQxs3UbHQMNgJ5JseD7PX75MVjLoBT5+mMNvLcvCMh2Gft5U3o1itvd6/MrP/QuahuBXf+3jfOa5Czz4vg/ybR/4Tt5670OUSjZLS0ssLS2xtrbKtevLCF2NISOnj7Z4x71HmSob2LpBeUQbsssuQay4drvLbiBJdJtyPd9iuo3cGG1pgkEMjz/+PC8+s8xz//UC//lTn0ZYNplh49g1NM1mo92hMdEaXwf7+14+jR4NpKJI4ro1tGqNoRIoLJDG+JqAEf0IKAmbNLW5etNDmS3M2iK3dgIG3sEEL+cHivF1WBzFVPDOCeVfSfT+G483M/Wb1jStMfq+DLwXeI28YH3X6Gk/wBsBpD8w+v67gC//df2pO4+8KOUnqNCHFL6kYqp2J4ChKF5FZIo50psopcYxMMUJGg6DEUUmX45OTDTHgfOF5KBSccd760L7Ubi+CxtMkdJwJ9whQ5IkIeefeZLb6yvEfsD2+gZlHd7znnfz8ssXOHHiOF/84uNkqeTq5WUsw6DX6TLXajGMFKV6k85QcrPTwyi5+PsBJQR6lkeBXHz1ElEqmZttcf3aKppeY2s34Pf+7POcvPcsh48scGSxxU63gwwDFqeb3HfyKE09pmUJ6Hncf3iKc4tTnJh0aVlQ1TUSr0/VqWA5Lv1McXF1nR0/5vb2Fs1Wi/7ARzddMhwc20WlkmajxvFji5w9dxJDlzx43ymqVsY77z1GRYXoUQBSUrcN6jY0ygYlJZCZohv4HDlxnIpp8/raKgiBKQSWMNB0KJVLIAROpcZjn/8yz58/z32nTnCs5TJRtbHNnDIzGOYxKnnzVuFULAxNYAiBY9tkSUqr2UQoiWNZTNRrVMoWNdehWrGp2RYiU9RrDokMce18VR5LwbWNDpbr4A19pMrBmjsDn0yziIOYnZ0e67c7fOnPP8nJ6QbDMObqps//+eu/zb/7nd+nFynW1la5fXuTctlmOAwp2y6Lh48z2Wwx05pHILnnyBRnFhrYcZ79FYeSKIFM2Jgll2qtwdD3iYcBk5NT9AY5TCMOQhzHQeg2QylIhEGiFJs7PQapYHqyxuRck6wEYRKMNEwjCxdQKjlYpk2mQErFcK9DSVMYQiGzvMAUej4hBEOpSBKVhwnqgudfvcpnnzjPQAqUDMeyouJGD4xbLwdQ3QMgqdAMovBvmesHzAFf0TTtIvAc8MUsyz4D/HPgo5qmXSfvQf3W6Pm/BUyOHv8o8NN/0xsUsINiYlcQU4rJQ6HROKDDHJiFoQizs8evUdBXitVUbkp2x6FpRXbzXwUjFlu+ou8EjKeEheK8sOnkhexgRTU71+KJJ77MwOvSak4xWW9S0g2uXbvOoUMLTEw0WVpa4uyZs6Rprvna3e3g73k4ZYdeENINc88ZQuC6LnGYI7GLIlypurRvbdGsNxn0Q97+wCOI2iyf++qzzB9e4MbqVer1Gmkc0tnaRE8lRxoO33LfWb7rvQ8zX7bIdrc4Pd/i7vkZlg4dZabZJAgCpBBEJYuk7JCNivL6zeugj2gsmU3QDxCZzv7ePrdv3WT52mtYZZ2d7jYzs00s2eM7v/1RKmZeeE4eWaBeNnAMgY3gVqeNYVu8fu0qKgjpDn1kluO/1SDEdk2SVOZwhijM42fCkGbV4ew9J+m2PWQSjnPkpQwpOwbD0CcYehTIcduymJhokMRhvqVJYuIwIAwCfM8jTWIMHQyhEEKhaYoJ20JPYsgEernB5vYW9oiLaJdssrKNadkoCSIFCextbzHdcJEKDNMmkoLL11a5dH2F9773fZw9exYhBK1Wi1ZrnuXl6+zv+4ShZM/3IA5466njfMv9pzFG/94HwzD/8j38XpeJRg0dRTgMKDsOMlO4joPf6+KUXZQuCDVJhiBOJV3P59rVZQ4fmUUzFX2/+4YVjWka7O97I88mBIOQqiaoGAINiV22xuLpLMuneLEuELqgYltYhspF145DpufcBQkqkQAAIABJREFUzKJ3fMASNA7E1qkaX2vFtZRngB3IkN7M8Tc207Msuwi89f/l8RXyftVffTwEvvvr+RBCCBSjLZVuEY2a1MYohC7PsclTJeEglaCY5GWawTAK0YRgGMVj3FDRKI9CRblskal8L10p2SSjbaNu2ihyT1+aSvQRZy9ToAsLXdeQUmPgh2OAqWUaeUyLUjmMQVl87Ymn2N3eot8TtHdX+Yn/+Qd57quPMTPXolx1eO65Z9Eswc7tdebnmux1emQJpJrgxpZHRwr8RJFqNmYc8PB9pxnuddhXipvtLppe44VX11k6PEusJMKRvHThKeYXZjl1+CH+82eepFZu0l7Z4J6js3kT2XWplF06vS1WNnxkplOpTfLs8mscqk/hVkOmKxb9UBL6AYYy0CsG235AbAuONWqQpsRRwF5vh7mFedqdTWquzTA0cMsO6A5ChNxauU7Zdjn/pS9w+tAia+0ur924jl2x6exuMePWEMKilFkkpqCnQUnlwtlIKSiZiDCkNdmgatlUyg43NldJswnMisEjDz/EHzzxPFpioeKYUlkQxAqnZFOt5Ims3W6XRq1Ge69Nlkpm5+cxIsHhuVlu3dzCsi3QBJot8IMAw7BwbZeB7iN0Y2TQlTglaPsKGcWUdIN4ECA0SZQoKs0p9jsSp95EODEnTi/y1JUVhGbg+4pUWnz6C+fZ3tjk3lMn2e9sYptw+PACSouRAt7ywDkGvsfN9Q1SoZioCU5OC65vCoRbQ8pcS4VSbLY7NKo1SmWbwPcxTYOBDBGaRUaMpRtEgxC7nJOXoyhkKxF85b9e4F3nTvPKixfQVIBtWkQYdD0fU4Bh5jdvQp+V2x0SIVDKIE0sIi3AtixkqjBLFiUth6L64WhCjiJLYpqTtTxRI/HIMos4UZQdgzSRmIZBlspcZG0cUJGEUUzR4791HdX/D0f2hojhYtVSrKA07WCVVXzdua0rpnjAG1ZahUWm2DYWaKsoyoWluRJdju8cwB1y/yJUP38Px3HGSvk4TigiksM4QDcUX/rCY6y9vopdcpicbhJKn9QKiNKAYycWOXRollrFRWWSrdttlBLce+Y0WUWw6XuokkuaGohM8b53PYgedZHDDvO2xYcefTekisnD8ywcP4oC3EYDP41Zu7HBCy9exLRdIgRnz56lwF/t7nbZ6Xls3NoiUxmhgnanR8VpcKvbw4hjmiVB3ZQ0Gw6mFTIIewxSxW6sc+lmnxdW2lzc8OmXa/QixfT8UeJE0JxwIVHMz8ziVl2OnTxOperQqNkYgce5Q7PMVWyU72OZNv1hTMm2GQQBwzBEZXkiwv6+R3OiTrDXya1PlQbrPY+9KKY1O89gELC+us6TTzyJlUp0Kxfn5hnmxmg7nkewOJqBnigmbJe33HWKfrsLEjZurI95c51OhziOqdcb4xWAECIn1SqVr55Sha3bVCs1hlFMqgk0YWE7DkEU5pAOyG0//ZCjC/MYWu6NyxAozeArF67Tk4K5w4ssLMxz5NhJqvUarblZLi9f58rydXTdIgh83KrDN731HBXLYGevS1+GDAb++HMC7Pc8qm6NTEE4jKnVaiPTflFgQY28gmEkaO+HfOGpZ6nMTtFsOqi4RzLoIvSDGO+iJ3x4cQG0fGWZyBDHtonCPEBQIMYtkCIMr7CLpWluFavXG+OVVNGeKV670BcC42u82AK++TL1d6RQFQ24At6YFx4xZp+lqbwjAvjgd4oqXQToF0ex7Cwid4UOYRQQJyGayFdw/b6XAxXv0FClad7bMs0DknLRmC+sAUVsS1HcSmULlQaULCCCcOgjE0XJqXHk+GmCxGPt5gqvvnKRLMkJNqWSS6YEm5vrNKdahKnAH4bUHQs1DDh913F2Om1CzeD2TocrL1/k3iOz7N9e5fKlS6ggZNj3yTSQaYjtWgwSnyAN2NjY4MqVTQAqFYeZhUMcPnKExcWj1ByLEwvzaDJkutXgVtdjYnoKUwfPG2DbZcqlCkpp+Psh0hCklk3m2Lx6s8tTy+v8lycuE2QOQaLo7Xa5+PIFlFL4w4Bqw6E5WWN+vsnQ77BYd5geabgS8uRVOYpAFoag1+/TqFeQYcDCTBPbtnj+8jIrux6X1te569S97Pd8SoZF2A84dWSBKA4IpWQwCO6IBMoHL1IppJIEUcjNjQ0UgonpFtpoMGIYBa+vNMrJkgRBkN+UDMH+MEAKGKaSiuWgZXkkb6ogikEYNrphYNkGb73/QUr1JgtHj/NPP/yjNGouJcOgWnVJMUgtl7984RI7fszWfshzL1xiEAeUbJthnK/gDx9e4NixoxxenCUY9JBJyOREk2qlNnZo7O11GQxyQEO/7+N5PvPzC6NgwbxwlMs2YRiTZSKHfZQESrfpJoLl1S3afsD84QWqJajZYjx82traolSyqNWcUQHKEfDBIMDQciq5NcqZhwN2X8EgKHYY/X4Oas3bIwUAmHGeXM7+KzyBaiya/obLo8qybNwIz3HqefWdnGyORWOFx6noYxVAyIK+WtCIbdse51odqNMVpVKuy5IyHjfii/eDgyCyA6hE/rmKu1bR0DfN0vhzFLDRz3/+MdbXVpluTJEkMZ7n85v/4XcJY4vFY0c5fe8pzpw5Q3/fx+v3OLJ4lP39HmXH5sa1dTQsdAWaDDCF4Opry0QKIt1lMJo4hp02J6ddvvnhR7j/3rNMVFw0BbOzLWIZglCkWczf+3sf4l/+y5/irruWaLWmeeaFS2xubRFFMTu329xeX2VpcZFKyWKgCV6+cg1dmJQMh2AwArVKMDPoDz1SQiQCTIeh6RDqFq/d2CQdDSGSRLLd6RCEIe32Fv7AY/XWKrEWsre1yex0EylVjotSksZkk0jGyCwdDz60NMY0BG95y1lEySEr2ewNhnz5q0/iujXiUDJRb9Jtb6GbeWqqW6+h63kzvd/38n6hIajUaximwYPveIhUE2zv7rKzn1/sExM5bDUIDkAeRThfagj0soUyBJmVT5RVqrBsG9O2QRh4/SCPFtbgwoWL3Nps89q16/zJH30CoTH2iMaj1f52u8t//OMv4Kd5lPLx48c5cnSRqakW01Mt4lhy/fp1fN8HE0pli8DzCXZ74xvp7Ows5bLN7m6X4TCmXm+ysrI6Xgn2+/6Ifp1fB5WKi6YryjWHzHAJEugOY15fXeXEkQUmKxYTE02iKKTRaDAYBNy8uU4YKhKZX2uGJshShY5gGARv6D0V4BXLyifjExON8c6muI6KyXgxwc97igdi7IPstjd//B3JTC/EnIpB4GMZxghFJLEtO6/g5byQyViO8FYWUorR7+bNaWC8sipOqBACHQuV5MxAQ4PMgGiUY5WOClFe2IrETsbWmwKP1e8fQB6KO0OaKpxJlxs3VvH2unR2AmLNot6ocWKuwUTD5qVXLlIuN3jp5Yu8/W0PMeg3ufLqRWp1B2ybXqjQsEGP8YYGRtnJx9LSQBAjMwNPSdI4ZmZ6it3tTcLMoNVs8Og7H+L3/uTTlEsCLVMsLCxwe+0yJ+ce4fbOLhs7Hl0JiQ+3Lq8ihE0kLDZeXebkdAtBxuHZaYZhzHYQkGCRxpKSbWCUBXFqkUoFiUJTIZpQKNOiD1x5vcP3vv9hXrl0OSf+hiGm45LEAXXHIpWShbvmITNYOjzLpRvr7HkOUbxPtVolk5JBGDLpOkgBd507g9AtvE4Po1JBpQZgMT3hcu3mOg9MnmSqNcvmhodlWfT2PVCMSDiCicYU3b0Onu8TScUzz14gs2B6bp61tTUqky0uXLmOaRo0KjW6vX0sy2Dl5iZpqpgRNipWGLbAH/g4ZYVhO4T9mJKZm4jRBYOhRCnBnG3h9WBjtY1lxdxzeIphIBkoA6HHpFG+8k4zwWefvMBcw+H+t5zm2MkFouEKAxFTcxtYFRuVCVLh0Ov3eP/9DzLUA45OLdDrdWm1pgBY32xzu93myo1VJqouVcdltd2m4jYIhyG1ikXfDwCbOFSgQnQZk2kWXU+h6Tb1EM7ds4Rzq8PK+jqWJdENi5IwODnb5PXNLoYVc+rYPJN1g3AYsLHlsdUJSBwXXwoqQlDWbZSm0EoCw7aJh2AY8djxMfa/klOiDamIw2DcUDcs6+va9sHfkTyqX//4r3/se7/3e6hUKjlteCTYzKdq2WjZmI6SNzOSJBlX7iiKME1zTGktcqOKlVKaSlKVkiqFaVlod3gGi310sVvWdf0Neo8D5Xo2upuYowKWZ0Sbpskw9vncn/0J3/TQI2xs9egOB8hYomRIa6rOwtwMpm6io7G+uo5pmiwdO0lnd5dKrcrFK2v4iYVpC/REkGmKre0tKnYJyPl90chxnirwenusrG8wPzfL66+/xvETh3n0nW/jcLNOGngEXpfLL7+M0Cx+5Ed+HJnB6zfWSDKwbIdhIhF6nrldL5mILKPk1Nnq9FCWoFyx8fr7JGman480RRcCLctJvUrTyLKEOAmJ+l2atRpR36dcqjKIA44eOYJTLuNWHDA0hgOf4WBIKiNCIRjGGZZZIQ4iDJFQKduEScrzly7z7IXX0EtlTGFSKVk4JYNk0AfTJAn3mT50jOu3dgmiBNuy0Me+zVHihsool/IbVq1aoz8MGAwGpKkiiSIqlQrZiBwdpQkl286JyJqGxAAjJ94EfZ9Gc5L9/T6mrmOXyoRhgu2U8wFKIun29/IgRSHw/H3uPXaCmbkWK+vX6UcJJd0cRRdHOE6V1a19Ot19nnnmOdB0up7P3PxRvCAh0Uw+9aXnMSo2Z+abNBsmrWqVumPiWIo46DFbc6mXLR4+d5a7Ds1ghD57e12UaREqRWZmBMEA27QQukGqUkzLAE1Dt8tEEjx/yPJrVzi1UEVkKd3uHsIsM+wPeeDsPcxVde45PME9Jye5e2GSejnDFjEP33cfV5dXKJUrKK2Erincepl6s85997+N+dYcUJj0TQr+Zb/fJ01T1EinaNulnKBs6iiV8rnH/uIbi+tHlpt7Pc8b956K6N5Cy1FM+oqGXLEFLJftsVn4zp8VGdcACIFuGjkseSR/KHQdY/JNctAAvDN6uIg/Lh5L04O8Kykl0dDHkJLzT36NjdtbVG2XiWqDb37Hu9CkYn11naEfkCaKQ3Pz6LrBysoq+z0fKRXxKGM8iWIohHamw56fx44kcT6O13SD/UFIOJS8//3vY7rVQMqQQa+LlkkGQx9FhGGWMS2Dkg3/6U//I1/6wmNoJYWmK+LBEFPTCEdxH7e3esTC4LlXryI1AXFA5PWYcBxKmRiPnIutMiKnUMtM0U1gc2CwN5RIFbPrdRgOAy5dusTNm+tsb7fZ2FgljQeU1IAzR2Zp6Qod2O15aLpBKiyCOO8n3n3XXVh2KTfqKkkYxXR2e9iOS5oJNm9vsbGxiYZAF9YoF0yOM4/KZQfTtNnf94hCyd6eh0oFrelZSiWbRx99lP393rgBH0Zxzv1TCplKDAwGA5++38M1BbLnM6FDKfKY1sBWPpal8BOfBAGWxUAqIiUIEsHlGyvUKjBVBhEWkbwKoVmkUuCWbBIp2N4N+eozl3n6/Do/+2//kE986jx//F+exYsV03Mt5udauLad/78NPDzPo9/vse91yNIAIUPWry8zOWnz/nfejzPsMWUpVBTSrNUYDHzKFSffopL3BJUMyTJBmlpYbo0wC9ENmGs2sISiXGvQ2d5E1wIWj7bY2G6zfGMDzaxx5sGHOLzQ4MGzx9FVQCZCHMdC6Plq9tD84nibVww2giAgDMPxYKvoNSeJHPe1SiX7GzGKWBvrlAqjcaECLwRkhVbqzp8VvawCfR1F+eSu2APnTTxQKBCCKImRSjIcBmPFeaHrqFQcSiVr3DA8UMYf8Md03RwXqCKr/fKrl3Adl5/86E9ilW2SICTwfG6urFMp2VRsh2NHjuP1PGQo2e957LQ7HDq0wNRkC33EA9SUQjDyKGqCPc8nDENsy0AXjBunruvy1FNPce7cOZqTDXq7XZ59/nlu93bwpGSYKDTTICYgFQGvv36R+qSLJKVWtrj72HEWZqcoWwazh1xKVZfUNAhTiS0EMxNNskjiGHfmehWrW4FhCnRDB8vk2uYeO/sDjIpJaiQYhk6rNY2UCc3mBG61TKPm0nRdrEzy7gfO0nRsskwyjH0ouchUUSmXGfQ6xIOYOJK54doS1OoN9vs+larLmbPnOHX3aUolZ6zDSZKYSsWlVLLY2+sShjH/D3VvHh7Hed95fqrqraOrqxuNxkkQBEEIBEkIpHiJ1GXJlhXJh2I7sSfjiePEWWcyORzHu5PNZHJtnn08m8STmc01md1MnDh5/Hi9ipL4UBzZcS7dB0VRJEWBIEXiIgjiaDS6q6vreKtq/6iuJv3HPo/8jOd5HDwPHxEUSAJN1Pv+ju/38+3rG8QwLAb6M/Z3lmLj8tJLL3WjvRRFpbe/SjsMSRQwChatrQ3UVFKwBGVL8N63T/PBdx/ie98xxUe//wQn75ikaCXotoFdKaPpgjCS1OoumjBw+qtE0uOTP/ljGcq3s43LQz+CZr1jChYIu8pDjzxMdagfs2TTTkJc18UPPCQJumETRBJNZDFqwrBoRJnt5UZtg4GRYUTRIPE8Hjp5lImKTSGBJMi+L13PQxWCKM5sR0Q+ugJpnBB4HqtbDfSCxeT4GIO9DrEmMIoWPpJTb1wA30P1fTYWF5k9fYrzb87yjofup+hkhm4/8Dqbc7vr+Mh9frmWMV+AxXH2b5Xb2m4FCuTZBG/l7bvioMol+/V6vSvmzKKsb86C8kEcZKd3/gVnqa1qt9XTNIGqgVUwKNgWKAmmEOgqmEKgcfOQy0NNITv184QRwzBwXbfLpMo3g/nnkwP1bNtm9cYaW9t1fvMzv4H0fFIBuq7S0+ewXFtm4sB+nn35OW4/OsOOPcOkKeyZGacR1LEsi63tEF2XmepZCTEsgyQN0WybyzUfnDI9ukEchxQ0g5rrYho2v/17f8TVJQ+zpx+Z6DQbIa1mlqicaBZvXlphsGeEO+84zD1Te3nv3ceZ3j/KzgGbogpFM+Ntr6xtgFDZXbV44PYxDvQmPLyvwoNTFe4fLbLHTilqKXEcoSkRqYzREhUbCwzB6ysN3lxPOHLHcYb6yniui6oKXK/Orp2jlEoVNNMiEQaXLl5gssdg2FKRiYbrbRIrGrWGR7Fc5cH7DvHwvUcZK1uMVyz6iwIZA0ED01b53GNP0PRqCC3JlOmGhet5JAn0lvtRdJXljUU8fBY3N9B0lemZGexihVassuH6tOOEIAV3y6dg2GhaxrbHMpjc0c+nPvgg7zg8SKUgaNQa+AGs1mpUbcn3HJnmUF8F/DqWsLKqomyx2fI59cpl5hbrfOnJb/CTH38U2mskiUuMJEFFMR00y6bU4yBjn5fPnaG+Xefw9AwV3WZioJ9rb66x48A0VlEwNjaCn/gUbQctFoSeh20YaGlC3A5p1xoE0kXB5fD0GP/60Xu4c7zCyckRqopP1YSCrlI0HFLNQdFAERKp25xa8lnYDnFjl3JJcHi8jIg9SrrFRN8wsSFQbRvTKTO2Zz9DfYOcP3MGv+VSsMo4FZvqUD+7do+hBBKv7RFGIZGUmB0ZUS5n0LTswBRCYOoG0vdJZIKmqP/8tn45umVwcLBjRA6724Lc95dv46JIdrGm+VYv9+Xlw3CguxK91TqTzzJs275lVZ0nLmefQ67LyjUgSZKldQDdgXuuxDUMg2vLi5BIPvrRH8R2LIQmkAp8+RvPcfv0YebnL3P02FFSJLOzF5i5Y5raxgZT+6f587/+GkbRQjUswjBL583Z07n0otHIkCv5TC4hybQ8aablMU29uwYWQmDoDnahQl91hGefeZm7T5zk7OnTNG5ssDC/zPnzFzrtT0h1zyS1podI4ODkBIu1LVbdgAtLm9yotXBSyZ37xpkerWLqSbedzo3YhlCJVZX59TrPnZnFdGw0I6FgGSS+Sn29jirA6bGI2g1MoRK3W4z09lBUs8q54XmUylXq6y5XlxZZvDSHFYfcsXeCesul1XYResadev8H3oVCx3yuG6SxTxpL1BRuXF8haLvsHh2mxxSM9pdRgwbLb85mAaBxjKmrVCoVNAVK5Qpus0UcSsqFMlqzxu6qzYmDU5iqzMJjoYMmMSjogoIiObJvjEfunqEkEhwBlmGgagZ2ucLsmytcXVhjq+nxv/3CJ/nUj32YyR1lCiLTJ4WRT7OZVco36jUSYGlpGUMTNLddYj/k05/5XS5ca2CUq5ScMuXeCnrJ4raxcZIgRE0TdgxUsQsWdsEilhK32WBx6TLvfuA4d0+NcM/RSaLtDbTQI05C2n6DJMmlGAmIMpfWPEam72Ji/37aQQ2UbIkShB4lq0y5Usnopk2PKwvLjOyZIlasjK7QSRvavWcC13O/xYqWJ5LnEoV8qZU/03lB8W10fcB3yzD9v/z+r33gA+/rHhBqp0LKGVC+38Y0zW5LBpl0IF9xZiLNqDP4jrE6AaVJknS3gXlLqesGftsnj+vJZ11ZK5h2S9SbsdRp1yOVdEIfFUXptoYX3zhN/fo1Ls3Nca3eJJUJ/QP9WHrCgYlxmo1t5q9exWt5vPuR9/DKuZcRScz84jKBYXF9KyZRDBRSDAWiDhBf1wVpmhK2GwwUi0hVJQqywFI/DInJhttCTRkc6MM0DHy/jYrCwsI8QmQQft9vUa6UuLFVJ447GYlxRKViE5sqm+tbTIzt4frSVcIQvEghVEwUw2TbbRFFEYaSEPuStioAJRMXJilx5BMpCpJMxa2FbQxDx/e20VINz2sQKykbm+s4pk6jFeI4JVpRgG4ZRH5Eouq0I0maJEQyZrh/hDAIWdpYod2OsG2TwcEB3vnOd/F//uHnkKmKpmhoaUyKDkmIYST09Fg8eGKGitrmtuEepseGOLR/lI9+5PtYvDKHLts0a9ukaRYSUve2GBjoRVUSSgWdYUfy6CMP8sRfPU6UKOzZvYe5ixeJO4G1gZQomsp2fQuRpjz68NtZuPomkYwIYolZNvG9iEiq1NY3uTR/FUOLGd+1g96eKqcvzJEkASWnjIJKb6WXIIyIk4RiT5k7776HY0ePsGvnHv7um8/w/g88TG19FU1TKfVUWFi8xsBQPwqZbMTdrrNzZISW26Sn7BD4AVu1NQJvm6HhXnb0V6nXXWrtANPKgHialhmw9ThESpXXzr3OwcndRK3NbG5k2PT1DdFutNh0m4xOTHLo0GHOvTHL3z57msQs45QcTMvkwB23c+LkCYRidJdLmVZN6zyfCaBkWkNTR1NV0iQLrtD0LGjlq199gp/5mX9GAaR54mu5XM5IBiRESkJIQiJuqsJzj1eSSgxToAnQBBimoOhYFGwDp2TT9vwMkIaKjBKMju4DTQVN7bSOopvrB3QFgfnsKj+sbm0PVVUllCGqyKK6VaGysV7HqVZ56D3voqenjCJDbqzXWKl5/PnXn6HXLnPo4DTvfuQhvvD5L6AHgtt2jjK6e4RT51awKw5Bq4EpEsxCFvIphEEQZTgM27bQTBUjlKgqiDTEtjK7wk994hPc944HccOENxdXKfYMoxcEmqLRM1hl1x3jGLbN2lY2RxGJQdLy6TEt7jh0nNOvLxG0AnoNSRj72EWLsN2gbEESemi6xfX1OpGEkZLFQCLptVR0PcFUQoRtYOiZoXstgHXfYfqO4wwNDeL0FOjdsQO3FeOU+xAlm74Bm0SRJC0XO/SZGR3FjHzUKKFglOnv7Wf+2jx1twGxwCgIqn1lWnHCL33md1F1G5JMQS0B33cpqHD3vlF++kP3o9aXMYMEtS0ZqpSZ3jvNX37+C4z3Ofz4v3kXH/3Qcd5xZAQtcBktCeoLK0S1Ou84McmJk9OkuuSBRx6lVBpENWDfgWn27t3fGUYnaHY2x3EqNmdee4kjB8Z48K4ZwmadNAYNUAWsy5DVazUuzK1w6coyw8NlTn/zS/zKJ36cPsciSuB628OXUKtlzoEn/v5J/uuf/DHPPvMU4yPj/Nbv/BEvv3YFtyU5dvgw+/ZOsHt0Cj9QkamBU6riuh4kklLBoK/q4LYaVAb6sWNJuRcOjFcoxwlhBH6YEEcSkYaEoQ9ISo7N5TfOMzY5jeM4SEKub9fZd/sMpmoxOzvHs6+com90msSoEsQqShrSM2CxZ+cEtlpF1zIUeI7wzjVWuV4NwHO9DE8sJYouSGSI9m2GO3xXVFS///u//2vvf//34vt+Z4uTrcRjGWHoOjKMulVMmqZoIlOW3wq/ywzGualY62wKsxh1RcmqD0VRkbFERenac6IoIo+Hj6Ko02YqnbZS6cyn0o4+S8G0rO4KVtcFV69cZHXpKi88+xy1bS8jEZgFKr29CFUlDl3uu/9evvDYF7jjyGFGdu1i9sJZenv7ee97H+Hv/ukphLBI0fCkpK9/FNf1SKMAxzRJ0wA1CInCEImKaVsoqkaSJpx+5TTXV1dYWlxiYs8eNjfWWV5a4T/+1n9C0VUWFue59PobCE0nBfwgQjdUNENl6tA0L7/6GncdPoSpwVZ9m7YETeiZbSROSeMY3dBxmy6aojExsYswltQbLfxIRRM6sUxQFY0kTmk3mhiaJGpto6Rk9hC7hGWYFExB081mGUKYGGaBxdV19FKJVhASygQZtUjTmELBpK+3Qm3zBnv27OLQHUd44YWX0Sy7e5EoikJ/tUx/TxFTjbALOlfWtkjNIgs3brBS22DpzTfRNJVyuUy10kfoBaipwtsfOMnU3nHuvXuG733kHk4eu4OUkMk9t/HlL30JoWksX18kaEfsHBnl8pU5Ypmye88EQRhQMAv0DwzitRoc2DcFSUgaRXhbG+iqhlFwCElpNFu4nk+carz+2its1zYY37ObM2dOYxhFHN1CFTqVoSG2t7c5OL2PjRubFIsV6vUtzKLDmQuvc+TkvSwvzrNVr3P85B24zXXsQoGVlRUOHryDjfUtVD1QcP1WAAAgAElEQVTzsTaaTTzXY/fUBKqUxEHKihegkWKZJlEoKdo27TAkVROGhnoQCtS3tzB0nabXZnN9g1TT0E2DcqWHv3v6RSyrgEbMgdt2ctvM7Tzy8HvxfYllmShqgm0XuyOBdtvrjAli2u02utC6s+cwDFEVhVarxZNf/wY/88m3Fpf1XXNQfeAD7+9qm1RFRQHCICCWEq3TnmWHRYqUEeVyuTObUdD1m1lnhqEjZUySxN2eWe2QGxVS0jhBRnGHCHozpgtu+r5M00RVFYLAR9O0TvtXyASkmkoQBOR5fi8/9zT4HjtHRrh0dZm2HyOjiOZ2HddtMbR7N+vr1/nQ972PXZNjPPG1r3H00B30lsq8+NTTDO8YYP7yCmgKmq7Tcn00slgvGUQUS0XKhQJh4NNOJKomsqRmUpQ0ZnVtjfd976MsLS6gqYI9k7v50lee4IUXXmR6/xS19XUUmbDttpBxhDBgYGSQL3/970nChJm945nuyjIJEpW2H2AXikQyS5sReuYNc0oOvrtFtVTMZjumQ+S1s2/+KAJFRTc0eh2H/p4Sge9RMFXCdgiJQtv10DQDQwfTKhBHCfUgwE8kulUgThJiJaXkFBECigWL3eM7ue+Bh4gTeP7ZUyQi+7fNLwo19fiVf/uzvPjC80QxrC1u4jdihvr66S2ViEKfjc11Wi2X11+9SLPR4uiRw6gy4NixQ/zFY1/AbzZYXZrntbNnCYKAOInQdIXbZw5imjYbGzWCoE3/4BDXr61gaBkWeWl5oZPFFxJ7m+y/bYK7j0wTtl226y2sagnTKtBwfTZrTTxvg7JtUK2W+I+/+euIOOXihQt4MmU7kqRhxNaWS9Eug6aya+duXp+7RDNKePqVM1xbXGL6yGF0A5Q0ZGjnDoIgxDAKtIMIp2SxsVnjyNGjrKxcQ7E0HCEo6gXOLa0jo4hYZpIF2ywSk2a0Ba/JXXcdwzB1BgcGqVYHaLhtFpaXKPf0MTE+ydmLs5RLNgdv38td9xxDLZYZ6B9BUTTixO/MPKPOMxjT9jw8r91VrydxjKIoHR2iQtx5bv/m63/7zyvXD+jelGkKJAlCVXFsB03J1ec5BjjpbuWALgMnxw/nyRd6h7wAEHUGicRZG3gzKOJmaEReYWUiUr87FMxL2nbb665cb40E6uut0tiuc/XyZQzdQC84jO4cZtfIIL19VZZWa8xduEJBWPzR//2H7J+cYnl1g3969hkcWzAx4PChd5/g3fcdoid1UcI1TM0jSiSa7dBuS3TLQreylOdmy0NRUnRNRVPggx/8fs6fPw9kn/v6Vp3twOenfvKnMFKBYVrsHB5BVaBsWfQWi0S+TxLD5PgIre060/v303J9eos2tqEj0hQlikiEoOH5WEWH7ZaHYtjYCuwbrmKmIb12GSUGy7JRhYFacDg3u4huOFT7K0SpJFZhq+kSpSpFy6bS42DoCV7Tpd9xcIzMnV8UglYoqXe2rQemJjh49C4++/9+hf/wn/8Q9JvSldyG8cB9J1DjkOXFDTYbPo5TZnTXCFbBoO27FIs2Dzzwdkolh6kD+2nJkOdeOc1rr13gzx//CoYos1nz8X2BnpTxmwmX5uZp+ZJzF2a5/OY8g8MjmJZNrKgcPnSYomVTLJdRFUmpr59GmBCrBmEqOX3mGXYNGjx05zRjlqSMi8DDKMByzeXi4gqLK8t88bEvUK9f4eSd+6nXVrHUhErFwbZtUgHL64ucv3ABVTVIpYoSqmyH8Hufe4xYq7BzeBrdskl1Qa1ZY2hXP67nc+/b7uflV05z8PBhCo6NqluM759GVxKKVubw0EwbN/AhAUsYBG3BC6/PcujkPUSpyvpmjUN3Hmfv1H5qtTqnXznDJz/5E0xNjJGoKqevLHPo4NHOdlzc4giRtFou29sNPM+jULC6izHHcbraw9y2lkH23nr/911TUf3Lf/EvUBWFNIkzq0YiKdgGKCkyynxhuq5jGDp5+EK26dJIE1AVlWbDxXFKhDLENAyUFISiZOKyBDQhiFO6W75sNpZ2NhRKltsnVBRVJSVFxhIZRbegjzXSVKIqCmHgAwlLVy+gKSrXrq3R9hLiuM1206XRauMFATcaLYbLZUqqwtsffIDmjVUaW1vEMmb3+ASXFq5CLKmvrrCrr8LM2ATCiNl06/hpmn1dbkS5t4ppqkReG98L0YVJ24u4eG2Fe++7n/W1GgNDg0QyZmVzixdOvYhMIgxTpeb62I5N2FinXB7gjmP38ur5s9x17HYKjs2pM6fp7+2lsb5OybEJ4xiZppiRRNMUoiREj1X8OCaSHsdOHmF2/jL1FJIogiRGKjGB7+MnCUVLZ0+vget5xGmMZmqM7N6JH4UEUUJta5tQtpC6ReIHaDLFSEGhjWWYHLzjCEtrW/zlk/9IECiggGbqRGlKGKX02g7D5RK2GfPyC8+j2Tpu6FMY3snVtTqvLS6zmSooGAyNjbPVajNetTDSlD279zJ7eZlYM2lseTQ3tmk2G0g/Ydv3kJrKxK5R1ldbPPw9D/PNb34Tq1jEqBRYWVom8NuMjO2k4W3T4zjsHBjE3W7R2z+AYZRQtQIHD+yjsbXAiSO3Y2kGteUtpBmzHas0pcJmbQ3LLrBnbIgf++gP8sa5cwTNBo1Epe23cVSI4xTbKZIkKVGcIoOIzRs1Ls5e5sLVFYK2zw9/5KOYukK5VOCZ02f5wIe+j+XVFbYCj95CmRsrWzzxTy9Ta4YkcUoCRLGkv1wikSHCMNALNutLNZ56/hT/6oc/ztlXXuXS2dcg0THNChu1Gqs36gSKRqF/kH2Hj1ItD2a4Ga+VQQSCgCiKsh9hSLlc6j6fPT0Vklh2ximZIj1JM5fIV776xHe+olIURVMU5VVFUZ7ovP8dzfXLq5hMjpDhSlstv0MmtLvSg1zvlIs688onjrNIrHxzmN+++Rwr+ztyWYPotnm3YmDyFvDWaiv/2JvQvptpNYZhUBkaZMtzcQMPwzZwHIdyucyOHcMUizY7hgZpRwkLq2vsHp/g3MU5duweI9YEiRCMj0+wuVlj585MtX7x8hxjwyPcNT1DVVGJ4oQQlfnVDeptye7J3aimitt2UXW45/AhvEaNOPGoDpS5Mj/PQF+Vf/UDH+Kuu09gaoKhaj9eo0lPtR/VFHz+i1/EdhyGhoZ58/IVSqWebJlQstluZUTIWEpSQ6Wnv5phkPXsNZRScv78eQzDyoaoVnZTWrqBRoJdsLlRq+FKODxziKJhYSqC6wvL1Dc3CDyP28bHmLptAjORFE0Do2CQ6oLR3ZOEccLC9TWeefEUebhAFyGdgGUIpPSwiwJbM9hcr9HnlBmqVHjx+TNsNxs4jkMcJVy8uswX/+IrvHp+jsLQBNU9U3zjqadQ1YTNzRqWZbBj5wjCtFCEhWU6+K0QVbEo2IJnn/tHhoaryNjHKdhEsWRgcJCXXnqJPEX7/IXzhDIkSSSRDGm1Gpw7fxYtVmm7LtKrc3B6hIohcETGjlpYWOPNuUUWF1Y5e/4MDz9yHz1lm76yjakbqEIl1CAIQ2QkiTtEz6NHDlHfbnDlyhUe/8o3+KVPf4av/u0zvHT2Cj/3b3+Br/zVk7zjbQ8zuWcK06ywtOaxVs/oIrppUCjYDPb3U6vVMC0Lz/PYrtVQLZsIlU/8zKd449IVNGGzsLjG4rU1qkOjbNTrqJqgv3+QifEJ4lh2Oxff92m1vC5qJ09+Mk2LcrncDUvNn6Gc/BnH/+PoCT9LhiDO375juX6KonQNxKqq0vay1kRVsnXqrTz0nAOVpc8kXbLnzdSMm19SzjrPDypdz4yiQFeQlnOotA6n59YI6jw84iYrK+kebHnQYqSoVIeHqfb14wceruuyurrKxsYGruvS3NxgdavOG0tr/Lc/+TNO3HcP19bXOHT8KJ4M6SlXmJyc5MSJ45imygMP3MPCpSuw1eDYrmFKpqAVhfipSqMN6y0PL5ZMHpjCKBpsrCyya2QA2zGYX5pj6sAUbr3OjuFhnn76KYgSLKFRsmz0gkXv8Aii5BD4IQsLi7RabXRhsbXVoOhUSFUVoVtUeirUQo83V1a5sdlganK8e7jv2DFK25PItk8kJbGSGciLQhCFIdvthNlra1y5coWtWg0tBSWSVHsrDA/1s1XbYKu2QVlNsDVIZIgXeCwszKMXbGYvzSOF1RUB5+22IQRR6HHnsRlarTVSKSHN/s7b905y9OgUwjJoNDx0DGJhIYpVhF3lP332MT735W8ieqtYBUHBFMg4pFav40WSWBfcWK8x2DvM5nodRZFIGbK0PI9pCuYvXyFVYHjXCNVSBUsTVEplenurTN8+k8WekTAxMUGp5DA8PEqPU6Wv12Z0yOBff/j7MdwaPaqKllq03ZDXzs2xdG0Z39vg/e99iMEeC9vM4qh6hwZptD3KxSy/z/V8ri4skwB9g8NYvcNcWNjg2XPzfPUfT/M//6//O//07Fke+8tv8Gef/xL/9U8e5+ylNdzYwI8lbmfAHbR9FD1DF1cqFcpOmSCWpGqmK7z7xAmCOEEUHCLN4EbTQ3XKlKqD3HvP/WiJ6OKTckROjurO1fi5tanV8jrPYdI92PO0qezXv8MWGkVRRoH3An/UeV8BHgQe73zInwIf6Pz8/Z336fz/dypv4ejMBZ45v/wmOtXoHhq3QrdydESueyoW7Q6OQnzLn5l5irL3c4yxrme9df7zPEI+F4FalvUtoL5b8cRB4N+yMQwZGRrFVAWVos1guczw8DBDQ4Ps2DHC1NQUjmEwPjXDquuzWXPZtXOM0JcUCw5nz5yn2fTZqtV56aWXELrKwtXLDPb1I3QYGHKoCDBEgiJUhKKxuNqg2DvM7Nw8QQD1MFNoj+wYZWL3FNutBkhJ1PbQTcGxu07wymtnQFfZf/sM33z2GVpS0vJDHn3v+4FMNFvpqRJsexQth0RVWdzYoBaCFxukisNDD74LIbLXvr7lksQqpiaQSUKsAmlC4ocIzWCz5XO9FRJpMDg6gm4aGdY3ldxYXUEXKiXHZsdwBaFKRCLpMQ0cHXpsgzDwUDuLlZyMkaaQZiQXCgWYnh7n+toq0jDAMAgjj9dem6MV+uimTdgKKRQMgsBnu95g7237SVLB4uoGpBZ2Ifu3rFQrRKnKduRy3/334G6t0aivISOVhx9+Dwf2H2LX6Dgz09P0DfRz/sIFTMOgYFhcnpvDD0NMx0bK7HILIx/Pc9nyfOxyP+12pqLXRcLJQ+Pcf3iSgmxkwklVcOa1Wc69eoE3zp8idteIZYhdcmjVGxmTTQMKWaq3Uy6jCoN6w6XtSZJU4EmVzZbk6rpHz85JvvIPL3BtO6GllfEUA8sAhMrA4GAmQWj7VAaqhDKLtjc1gUZI1KrxB7/9GZqrK9SbdYpVB7PHRisb7NgzwaMf/AHCICFsZYEr+ezWsqyujebWSzxv/bJn7Oa8+NZuRPk28rLeakX128DPA7k5p4+3mOsH5Ll+/79v6S1ArVwvlc+hfN9DUTO9lGkJIhlmoaCKQFOz/wohCIKQdjtrCdM87ThNQGTzJaCr+k5JCEIPGYdd46ZuGqQKhL6PkoKKStD2O1VZ2NVUCQxMzSKVIZqS0Nc7TJwmlPod7n3bYXoHHTxZ49LCLJcvzeN7PgtnziLrIX/9/AX+7HOPkbY9vvn1JxkaG6dWqzG1bwbdqlDqHWVrq45uJBRslVptjYN7B7FViVCg6dUxCxZN18NyKkjV4Eazzf/1J5/n5Mm3MzV5iN1jk0wfmeGrTz5BmiScvfA6ExOjBL7LP5w6A6nAlFAuFPjylx/HsgSe77HtuZjFCp7n0mzUiDWDSlohDhNCXfLrv/ufibSQRCY0t12kmiAVgW05qBEYqkVo21mopWOzvuLTagicUpl26KKl2UE/NDyCU67iRXRMu1DtrxIpCZFiUL++xp4dg2iGIAoTbNvIGE9BNpgPUijqFo0rK6QyIazXqRQcvFbIxN7BrMqNQhQDAh92DI5StGw2Vy9jBi67y4Jm4KIqNlbBoRWFkPiICJ7+h7/HsAy+592Psumr/PafPsHnnjjFZ//yGeavLlI0HN7zyKM4vVXKlUHKPTa7dlQ589xz9FQsJveNc/XNK9m8TXVQFIHjOFRK/Zw5fZowctlRrXJif4UDu6qYwQYFVTI2tp+6m3D/8UNMDFbYrjdwHMFAXzVL/pGgaAmSTFvoJ5L+vjK+71MWZcqxjdAtXjx9niA1UDWLer1Oo7kBikRPVKK2j64BWoIVSUxV4PQ6yKTODjPkwaPTnHrhGQqDVWynSpQkKCpUevo5fOQualsNGu2Mm5WPaDRN7VZXYZhZyvLherHoUChY3WVV3pXEcYKuQRT4fDuMz7eSQvMosJam6Stv+U99C2+Kovy4oiinFEU5tb3d+JZKKDtwbrZdafd2TbqHTh7Brih0LTK6LrqHSt7u5SbUHEucQ8ByEWcO2MvtMnnQ6K1wvVtd4LqeeQkVRUCa3Qy7xyYgVfHbIb/0yU/yMz/6MaZvG2Pf1Bj2YJnenYMcOXkcx7bYe+gE7/3+D7Njxwg7qmV6eso0Gg2klOzePc7E3kkOHzuKqots21dQKVcqpKmKoTsEXohhWFmggJYxvwd6K/zO73yG6f1jfOyHfoidO0eYmp6mUOmlUCrT1z/I2J4JlpZWMAwD3w8YGBjk+PHj3RswCHy2OxmCxYKBBjQTH80SqEnCsYMztGouhmawf/80QVuSIgmCLD9PxpnlJGx32nahYhQctrbqKGkCWkardF03+zVFJUxVykOjXFxcYaXWQNUNhGEQtLOI9WLR7ppcs8o4wbYsmk2XsfFxUlScUpn1zQ0q1SoO0GtA0ZCoSkilqHL1zbPUNlc5sHeYd95/mPe/+z2U7TLLSytImZDGYOoWpZJDtVqlULD48z9/jJfPnGdpdYNIEaDbnJpb4fiDj/Kzv/IbfPFrT9GzYz+Xll3W3YTjb3+Qof4xCsJmeGQYtWghZcji4nzXTjI8PExPT4WdO0ewbZuxsX4+/vEfpLficPb0S6ytb/DU86c4eeI4k7uGido+ze06qcy2ZqQQS4llWqid7/mMZBtiFSzcZoPeSpmSY+O6Lv39/RSLdgfwaHUPl2LR5kbTxaqUadQ3ePvbTvDTP/Fj3H3iBFfm5pm/soJStDHLDiMTE3zPex+lWq1iWVlKU04Uhax7cRyny4fLnms6hYPfDT+JorA7w8oDgb9dcN5b+eh7gfcpijIPfJGs5fsd/jtz/b4lgLS30tnqiY7nLkes2rd8/E1PYBD436Iiz8tJyHjrOZoke5FkN105x8XkL1SGHra6rOdM9n8ToZr31PnhqGkakfS7PsM4TkjiTI07OjrGwsIiv/Grv8yLX3+SsiJZWZ5jc3uNa1urvPTqKbQEPv/4k3zi332aVNgM9FaYn7/M3qlxms0Gvu+iCpWV1RXuuucuSj1lbr/jMFeXlrMqMlURqsBr+bQ8n1jR2Kq5EMPI8CC/+Is/z6U3Zvm5n/t5pg7cjlWscOHiHA3Po1Aq09tbxvPaFAo2V+cXOX36NAMDg93XTjcFPRWHfVOTGJpKoIQEMiT2Q2zTxgQKRYcXT59GN28uLOJEkiSSsO2hG4JAhhgFm/NXFnHKVYoFmzAK0TpI6D0TY4yODtOOE77x/CybsUDqNjXXJU4T4iAE/+YQNpeFoKqIFCzdYH2rhowT+gYGCcKEc+cv8K777+fgnkEKisuH/+V76C9Kfu0XPsG/+fiHOHJkP8tL83z9a98kCSV3HDmK3w5pNV2UCDY3a0gpKZXKfPCDH6JYGWRwdJxEEWw1PV5frvNDP/0L1GKbrcTm07/3Z7y+7PPYN07xC7/x+zTbgoX5FZQ0oX90lCT1mb59Kgv/SGFtbY0g8Jmdnc2qj6LKc09/gz07+rlj/xS+12BodJwXXniBfWODKKFHtcdGQRJGHkqSQJwZ7FOZX6SCJA0RekK5aJPKEOUW4kjuS82eRbq+WadaZmNtkV/7pU+xd3yEUy+8xKmXTrO20SBVbULTINAM7n3Hg1jFMl6H8plf5JAdRpVKpfts5tF1+XOT48AByuVy10ubj3SCwP/OJiWnafrv0zQdTdN0HPgwWU7fR/gO5vplMgG+BbsCdASXaleWn9lZJJVKhSiSHWxx0mVm50P33HKTz5xyk29+wuu6+Ja/I+dQ5Zl/eVWVH4A3Y9wTVC3BMARxDKpioGkGhw8fxTAsenqqWJUq1Wo/M3v389k/+ANGbJvRoX4mpyZR1Szqe+LAYYp9Yxy58352jAzzzDNPUe5xqG/XKFcqxCksLC1jOw5hYjO0YwyZSoQhuy2UMAyCWCIVQc/QKNfWPUbGZvji44/xmd/8LT72kf+JEzMnGBwZoea67Nk7RbPpUihk257eSpn19TVaLbf7ugSdm89zG6S+n9ljNBVLWDz36hmKPRXaKmxEPpEu0A01I1VYmSWp0mOjkmAYFs2WT6iquK0sfy9VobdaQVXh6tUrtLwGvWULq6SCZhDFEj8BYVr0lBxscbNVzx647HNMOqb0nv4qUQJrGzVCmbC6vsFzp88wXK0ys3cCR6jcObOfxz//OU6/8ALPPH+G7WZCuXcEvWhz6uXTqKrAMR10MvV6jvD5m7/5GmHgMX95DtKEkeFBSrbDkUOHiXwfXRU0Ap/NlkSqDjv3TPOX33iGGMHE2DhnXz4NiuTpp5/C8zL7iO/7FAo2+/btp1RyMLUyA9VhBipl7rtzhj4j4erSCtdqLq9fmOXo7dNIz0UlwW3WcGynE0ABWid813FskiRExh6mIfBaLj1lB8uyaDQa3Uiq7e3Gt+BYKkLy4fc8yOob51m/tsbCSo1GmIBt0bdnhFK5yg995GOYwsZQsuixjCGVXU755X4rOCB/7aJIdhEueWu4uVlDUegM32+GlX47J9V/j+DzO5brR4e6mLdwKQkoCUkqSdJMjS2jhLAdokhotzyUNKHd8hCqShpLhKqiKSq6lm0bMtJndpOUSs63sm9SFUO30IWBZVgoaTbXyn6/ipJKhAoksiOsTNAUshYmFZ0QCgmKRDFVzJ4KWtFhx8QoiZpweWmDl186z2//8q9y9+0zBFs15i5eAGGwtLLG8kaNz37xK/zif/gjDh97iE/9+09z7K77KBcrOH0VUEJUzeCLf/UMj//tcwQJCF0jSjvM93aDuFlDb/v09tlcWplHtW1W1xsEUcjZ869x8q7j7NozzCd+6pOYxTLzyysUrV6UBMpliw9+8FFUw6Dpe4RJAppB24ftbYkeG9w7M47ZkgwYKiP9/aSxgERlbnGDG82McyTikERCEEpUI6Hteui6QTvwUAyVqxsNnrm4jLNzlFLVRugGRd3CMSyEIujtHyFJO9ywOEFPVVbqHiEqvbpARF7nBhcoiQppiKJDqqr09VUZHnEwCwmGKaiUB1ndbPDG5WW8zQazz7/Ey6cuMDNzD2oq2FjcQGgGrgxZXKtx4MAEvVWH67VVGnGI10FTFy2Dn/yJT9FnlSmZBppIuL6+QeyHvHbqNEXDomhYGIqBVbTwpE+t1qDmC1Z9g+1AMDU+zNK1ZXTLotxT5dq1DaIEdKvCS6dfQNHKDA8OoqqwY+cgr509za7RflIpaacqQaHM+dnLHDs6w6G9Y5TSBLddQwoVz3cpmgmGCnHbRxgGW56PkqpoiqBeq9NTsDA0wfZWA6dYoViqEioJkRaya8TmR973LlLP48r8ClevrpCUyhh9g4ztn+b2Iyf4wY/+OBJBoWiDklVhpVKWi5kvobKk8owRny2Ywg4tQXRawZuLLsMwuir1QsHGMCwsy0b7Nto/5dtZEf6Pejuwf3/6x3/8h0AH9q/ePFTiOCElS8AtFR3cRoNQyg7kTnaZ5nkiiRDZJirP9cvnVXmrmFdHcJOLnldNigJap2rLfUmQdvvqPBw1iuIu8qUdS5LIp+3Wef7Zf2TuzBk2b9SwFcFATxnDMugbHWHP/hl+/f/4LQK7QpqC3/BwNINWcw1FB9sQHNy3H6NgcO/9J3j57Bwvv3aZAIFVsFi8+Dp3HRxnz9goJ0/McOniZdJIYeHaKsW+QV6fvYwMEloixW166GaG19g7NsKP/MjH+PrXv84zT7+EDH3aXoOf/qkf57H/53McOnSI185cQFMNohCEkjA6WKWvbLPaquNv+yR+yPTUBGevrbGyXieMEiwtgVSiqgZxAhEhJV0QdQIR/DC7eYNGg/feO4m7MY8hDMqWw+DQMJ6UrNyo8dTsKm5iYFsWrutlmrnIZ//uYa6srBEKh6bnoaugGCqmULn/6DT9juDq0gqeFyKjTMqysbpG/2CVoeF+fN/j+vVlSqUqdlFgWDb1zS2CdkgcZSSENIVdu3axvr6BqQqkX2fX7lH+4qkLOJVypnVLYGLnOHNX5xgYGOTGjVUsy0LTMxmB5/lYWvYgRq0aD90zw/Sufg7OTPHccy8wMjCClJJas0ZPT4VGLUuP6R/qp950KVQGefbF81R7KzzzyhzNRGKXDNQAdJEwOT6BLgzOzZ7FjVSCKMHSDUgzYkVfX5VWyyPpzI6iSGLECQXHoeV5CN1CsyS12ga/+vP/CxdeOcXa2hqKJlC1bCao91QIZcL73v8BVN0gknQxLTmVM6+Ubk0Nz7f0mpY9H/nWLx/h5OBJod6cYeW/J44TPvajP8Ybb8y+pbLquybcIWek52jgPDY9nw/pukGj0UAlexHzGVEel5XHawkhMj9fp/zU9ZtpM76fz5dE14qRpXfcJBMmqewKSfMzPI/RiuNMO5KbnpMkwVAFqbBQimUUzWLH7gnSRGVrbZWr11wU6VNcXub1V88wMzWCMz7N6edPk6iSXbdNcuXNbF3cbvqcm1smjiWvXF4kUg3iVEXTDKTn8cmP/wDCWyWSPouzL5G4PmWnByvaoC+xmei1OXziBF/++39Cjw3aYYIwBefPX+AXfwCaG9wAACAASURBVPGXqVR6uyJZVc1aHV0XvPHGbJfVZZjZEHh9u8HK6ir9A2W2N2rsHq6yvl1jaasOioqhSIYci/GxUTZurOG2feqJQEsllm1TqzewbIe26yJMwWqtwb0HD7O+vob0QhYXFzEch76+foTYIA2yNOZSuYofSSxF0HD9LDi04eNH2RxMiVU26g1IVU6fPkM7VDuRZjZR7FGxs23s1YVFdFNQrVaJ4+yievPqFUrFIkmSXW6Dw8MEgc+5c+fp66ui4TM5OcHQ7nGOhAbn5+azBU+ccHXuCnrRIgglBdthbGyMulcjaIcMVAZZXd/gfd/7Pr7014+zUHfZt28/L586Q09PldX1VSzDoGA7LCws0ttToe2FLCyvYFplpvYc5rnPfY2hIZ9tL6TU59Bo1rF1h4LpcP7iHOO7Rxkd7Kfe9NlshHgSVC3baLdaHqWiw7bvoukCTRcoXjZvrfSWabU93vbAcd528h5e+Lt/pNX0cAYGiVFp+yH7Dx4ilJIjR49jFGxkDEnod0cjkG29AWzb7o5YAPLMzVv1izlkUoh8XJMN8/PEZCklhaKNKvjnB85DofPF3dzM6brRnRvdVIlncUD5iXyrhiNHSuQCtKw9u3nQtVpuF2ecU0Pzgyi/KYBbtos5oyrpfj75xyiK0v2zlTyVJoH7HngIy7axe2w0Q6AaBoVSP5pqUTRsLENwYmKEfTsqtJsuW/4GqgqDA8MoqcreyRlmjh2n1gwJg4Sk5aHJkNjz2DHYDzLEjwBhY9g9NFqSvp0jmIaKErtcvXSG3Q48et9hrKCG7m0AKnGcsrGxged53a9hbm6O48ePU6lUKBZtJicnaQeZ2jhKE1LdoN2sMz01ys7d4zx9YRklCGm3PA7cNsrYoMPG/Cy3VS0meh2Ujviv0WhgmgbttkexWCbGYO7yGtevrVCv19G0rG3r66ty/foKQQefa5oWYZwg48w4K3SD9nYDv53JU7JliKBgGVy+fAVFEQS+pFBwOr4yFYRPio+lJVQKFvUtj5Vra7Q9SbV3kFgRbAUhjRTW19eIIsmhQ4coFh0iTbKyvsrLp0/zxsUr6NxMUqnuHCaME/xIEsiEq4vLrK01aG17uM06lq3y5S9/iVQ3eGNphS9+6Ul0Z5Dbpg8zc+wujHKZnbftZ9fkNGapn4ce/QCh7tAO4ctfeRKwmK/XUSwLodnEkUWiQtML0Qs2y6vL3NhoUC3Z9FgqjsjAdYWOhy5NE4arVfQ0wQB6eiodbDeMjAyixJJzp8+AIghNB8V2GBgdY3JmhhP338/hI8dRdYMglCgdjVO+UMoYbrm0IJv/ZsJpqxvom3cgt2odFUXFcZwu9DLPy9R1A0XLktG/rSPiu6P125d+9r/9FwzbZn1zg2q52s3tyxXk+fAOIEV2X0zIDjhVyTccKlLeDC5VFDXTkujiltvg5oBcFzcH8lJKNJHJI6yOiTOVCWkad0B+YWcISIfQkKDq4PshUiZZiX76GVbmF5l97Qz+VoMg8oDOzeeUGRytIqTNnffexx/86R9m8UtqmdANKWkGviXp7+8nCHxc18VDYLZd/t1PfgT8NTbdBNdtoBsa9XqNnt4qm+urCA3SWLK5vk6xt4L0fQ7cNsWLr89zcWEdNxSgSIJEUik7DBfLNLw6ipYQBJLe6iib12v4sY/QE6qGwQfeeQ+n5i7z9Nk5kijh3oOTJNLj+vU14sRAM1TUdoORSplK7yCvLi2S6A6b2x6GpuIpEqUdcuK2Ckenx2jcWCYKVdJEIDQLiiqzy3VmVxp4sZpVp0oCMmFn3yCp9ChWbNZaIZueT+olfP/7HuT8q6cQqUrJELhugziW9FYr1G+sMjQyjmoKLl6Z5baRceIQRndWWdyooShptzpWExXXdWm0WqRCZbisMjk5xc//8qeZvvshFKdCGCTYhkGKjy7KKGmCDH1GR0ZY3tpA2BYbtTrD5SrrmyuUHAdNZIuBspKFYISux50HD/PC7EsITbBz5zizb8xR7q1QrVZZXlrBsmwSDZIYTNPGa/nsHBvk2vIaQkj6B8psba4xNjzMbTtHOPfaGdJSlTTJAkd7eiqEoYe3WWdgbBTLsmhu1+irVLjv7rtoNDbY2KxR7htmtdbgyJGj7N07dYuQWc26lRwhbOQtXHY5C/Vm0nG2fb1JGslDgw0jC9zIA1lySVA+p8q1VDcTlSU//NEf5fz5199SWfVdUVFlMUywvV2nVHS6uNvMM2R0csK+lZJw0zJz0wOYz5ryFwbolJsJzaaHogiKxXJXF5XPsfKt0q3l682NX3bb51XWzRCJlDzFJle6B0HIzLHjaI7NbdPTtBSJMA1ikgwpa1lsb25w5eoc//C3X+Pdb78H0xRU+xxiTUJRkAYh2+sbrC4uI5KsYhOGwbmLcywsryE0mUWHS9AwsAyBqsD+vVM4dpF7Tt6FY1mUig4LC/OcPLaP9z1yF7uHBKZQKeiCIJDMzi9SNKposUVBGAxXHUyRYCARUjI0WOWvnnqJp8/Okmoqjp4wOT7O9esbWKaNAELfY2hoGK+dDVOlF7K1XsMUKjLy6S04DJQdKqZNbWUFVbOIpCRVEgpFi4GeKv2OgxJLLNNBxiphoqKYBiubK/RVLXbuGMb3fGyrgmUl9FUdpg9MEkuP+nYDRVMxLIvtRoPR6Rm2Wg2a7ga7R/oJ4xCZhly8dAXTNP4/6t48SM77vO/8vPfRbx/T05gLg8FgcBAAQRAED5EQRVEydVGkSEuy1ivJ3lhlO3aUuOxN4nWl4qxSlaSSirPZVSTHp2wpWlqWXdZFaSWKFCmKoigQJEEQBEEcg8FgMNPT09Pd0/322+/5e/ePt98GmErWVG2ype0qVl/AC/bb/T6/5/d9vgeNRgPP8+j1XDq+y023HmF8vErRMBn4gnany+/+7j/iHW87TEnyMYIOO6smSbtL2FrhyN4a+3eUGDc9dhVlLLfFnGNT0nQmihV2zMyiaTo3HTyCbDkohRI33n4Hz758Cqs0BXqJC5frFMYmSISgvt7EsGwkWUWgZwONIMYpV+g0G0iJy2TZYateR1FLXF5pECgqOw/up1CoMjU9x4GDhymXJzAsm32HDmFYNl3fpzI5hVkuoZYdatsmKJar3HLbbXzoZx9m166FUbBvGIbD1GkxkrFlBUodTc3zwpNNxNVRdwQM7ZKy66ZSqYx0mTkWle9o8oUfGIX6/iThDj8lGFWm9TMNE0kI1CFAntu6yHLWRmbgNkPpTDjawqlqxmLOJTI5STTXF2UTvuzPRmE8Io5mHZk8bF+HBnyIUUubpoy4H/kWMD+52ZcjkYhcg5ivKj7vuO+9PPq1v2Zi1xxLp08PLS1gs93kyI0HEVKThb172Fxf4aH33MfjTz2DSLqkVgXNV7nrjjt5/vnjuK5LuVJibtdO1lpd2pFHz20RRYLa+CSGphP4LqWCw6DvE3oxzWYTU1ZxxisEYcjmxirr600+/tB9bPZlvvgXXwLZJjR1rqy3MA3BzJTD0qVzCAk0Ffbs3kMQ+CxtunipSuqFvP3IPn7wg6cRqk4/kVFSUFLBaqNBQVbxul327pihHcLVZgvNNgn6XQIvprCvhqUKvChmYe8e1tbqXFw6RxjA+x96AKtc4pvPnkZTTGy7xMDvMjNdY6vTpDeMFw+imDtuP8L4WInXXmpgqDqqqbO52UDTVcrlEheWlhC+z+65KTbqy6hmFdPQUROVyclJoihidXUVx3GQZZ0f/fg5SlaRhfkFLlxaRNFMCpZOqWoj9T12zMwT+h7HDs3y7vvfy5/92ee47bZ9DHwXQzW5vFwniFU2uh7ttSaNNKQ9CHE3XcyijReHvPz6BVLLpj8QqKoJCghJRlEzQFwzbDzPJxEZ1hrFPqkIcdGRrRK9EMzCFJqmMgCee/Ekhw8fYtuUw/TULJcvLxMEIXsPHKS+XqfreWzbVmNiYoJdu+Y4fOttEMTs8X2iIf9K0a6lFmfXFyPqQU68zqd7siwjYjEqUIqiIqvyCOtN0yxteuSjP8wayOVtpmmOouxyzEpV85i6N49R/VRs/W7Yvy/90z/9A3RZRkbGj+MR+J3bnOYEMkkCWWHUPo78pCIxLCoA1/bMWXGTR12ULMtEca701iG9RhYVQqDp11aENM14O5kOUBlp/PK9uBCCSARIUjYdCodyB1mKabdW+dFzT3P6heP03Yylu7y8gghlhKwyVrQ5uGuWHTMzKJpMww35v545gYY6+uwHDuznlRdP0Jcl7nnLHWyzVG7cWeWVV05SLpdxijZbvQ6dzS437NlPySly+syLjNkO03Oz/NLf/VU+84d/ytKFC1RLDv1uF9Mq0U90/vLxZxnfPkOrWadoqow5Fdp9D0c32TE1w6nz54giCFMwJMHts1N4SsirV+qYlo0e+Hz0oXfz5NPPQRizq1bCT2ROLa4QalnyiObYSAOPdxyZx0w6tLZCymUni7sqOnR9laLikShw6O538rkv/A1XGmEmu3E7vO3IYbpel4vrLdxEZrJsEw9cKlaJeBCTKCFHjhzm7OtnsW0TW9O5fGmFgqGyc36WK40G49USfl+w2ljDtm327dvH8vIy1VIVPwzobHVJZVB1kzTwKehgFErIImS8UqOxXqc2XmJ1s4kiy0SBx/TkFOMzNQbtLmPFKm+59x6ePX6Gx144TqDYHL3hCI9993EOveU2nn/hBIaiUhubob6+SqVSot/vkqQCTTMZeNlvvKBknL5iyabndtDw2bdrFsmPaW908BSdVNMxK1U22x2++MgXWFurZxy1VOAUK3QGLiXDJul6JFKMZuiopkoUZAVEZrj90kz6fXdUREaGlUN1hzTEeC0r843KC1V+vaXDRXkwzB7IMSzIFvYc8823f7miI58K5pyrj330f+LMmdfeVLX6qfCj+v3PfPZTDz/0IGEUI5BQNYlEZC6diiqRiIhEJERRhGFYpEmCSARSCvrQ9TH3qVJVBVXO5I4iSTB0A0VViKIAyIIZNC1LPJaQRnvt7HUVefj3ZCAOI4SUouo6URwjqyq6pqOq2nDCqBHEMrKiMYgCUMFRIVJiUk1mrFwhcmOWly4hxyqOUiEdK7B9chLHtOl3BzjlIoNeiz03HuSJEy9RKRbo9wISwPW7SLrGdLHEyy+/wpnzl7j77tt4/exrmLrOVnOTsUIN1/UwHYcr66tMjo0ziALq63VWlpeIQp973vZ2Ll1axZqs8NrpU7TX1ti9MMk77nsfp8+8glZwGLgeY4UyhpIShB6dwMMIUgZyyvZShSToMFA0WlsBQRBRdUziQZte04VQkKQ9VGQcp0Df8yiNj9PZCtg5oTMzptHZ6lMqVTl69FZkRWG9Uac2VsAuOkxOTvP8008xXdHYMTfP6uVVbprfwerqMo5Zwo9lXG9AmqSYlkkYe8RSiCbB+LYKq2t16utNbDWlWB7DKI7h+imDnofnBli6QaUySbO5ieu5VCyby602kYBUSGiySkk3MEyLVDdZuXwVWymx3u7iVMsUKwbRQGXgetRqNZrtDr21Jv0k5srmBludDhIeu6ZmeeXFVzlXX8cdePS3tnB0EznO8NY0CQkCF0WW8AN5ZLOtKDJlW0WJOrznzn187MF7ufPWvXzsofvpN6/y7rffzqDVxLFUulubGJrMC6+e4ec/9nFEnGBKGkJO0eQsHzOSAgzLRKQJmqxgFx0KjoOqGyDLyFLm4+b7AQCqYyFgxHnKdjDFkUuJpCvISuYnFUchkiwRx7lrp4499H7TFAUpTZFVBSHSkf+UNgxzyDuoTKKW8JWvfI1PfvLNJSX/dBSq3//spz70oQ8iSRJJkhBFwbDNVIYfbrgSDEG9wPdHaRdyJrwDMomLLMvIUpaSklmfkuWNGZmFbEZFiFDV7L3ciiI/qUkcjRTfkkQW5BiFQ9vXLMEmCx9NkWUJnYTEH6CLBOF7pJKZxSeZNrXKJErFoNFu8Nq5lymXLQ7s3cvFCxdAUhCywVavh6LAnt3zKKkgiiJ6ns/uPftYXV2BUPAbf/+T1DfqXG03ee/7H+J973uQdrNJEgt2LGyn02nRXL9K2O8zNm7hBwOiKCb0Q66sriKSlH5/wL///Bd47cVXOHjDXtrdTcLeJmnQZ6vdplQostF2CROBqhr03RhFlhiIkOmxCkVLY6W5SSypqEhUDJkkHDAYxEzWahiagqnrLK00kHSNzW4Pw3aYrBjMT49RKRXptLZotTbYaNSZmZlGUWQajQ2azQ1mZ2cZhCFr6xvcdmg/W2uLjI/ZDEKJRtdD0k28JERXFHZMTNJeb2CNFwk8FzmGtxy9nSAaEIQRm5st+q6LWTKpVMdYvnIZVVGIkxi7YBF6A4qVElEYsV5fJYljVClFSILxbeNsn57GG2zR6feyrmtzA02CYqWMaVlIaYJm6vT6LpZtoUoyrfYGQRhTrk6w0thkcmqG1mYTCUjiBOQUgSCRVISkUClrDLbW2eZI3DA3xt5Jk/vedivFgs7WVpsb9u3j0a9/jYmJCdbW1lCLBsfeeheOZZL4A+pbDUKRsmNuD7FQ0HSJdruFqipomoKuaZntiqoQi5Q89ToIfBRZGVmshGFIkISQptiWjRAJpVJ5JB3TdZ0gCkmFII5CdE1HH24T8yFXGIZouj7MRYIojoZ/VxthzVEUASlJkpAkmRHmV77ydT75yU/+/6dQffYzn/nU/fe/Zwi4JaMPmQyjo3KJjSRJCJGiDomXWX1KEalAUZQhWJdpcbJ2NsUwdMSwAGT0BOMN4mbIChpkUVmqkkdhBRnVQVNHXZgsZxOQbJXQSZKEQLaIUonZuV1MTW1HTj1qToGCqiEjsfuG27n58FFePfkqly+ts3r5dWq1cWrT0yyvrjLwBsiSYHNtjR0TWYLu17/zXeb37qex1qRaLvONRx/FHbiMz2zjq1/9Nj/+8fMcOHCIb377CX74wml+8RN/h0GQsLnVZ9f+/QQhqKrJVm/AVrdFvb5OEAQ888ST7JrfxQcefpCZ2SkWXzvJ5PgY8zMz3HzzTbz8ymk008ALBFGckiAjGTIz5SKe79IehIQJkMTcvG8e1x1gGRbdbgdFlYmDgG3TU7Rcl3JtGxutJo4qUzZgY2MFFY3x8SGfS0rpex579uxGVdUsBjwMuOngAc699iqHD+2nWt1GY6Cw2ukRphGpIiOLlH63w/bJKbqDHjfsWWCzvknkRwxiH8u0WVu7yszUBBvdFlHoUyyXmJ4cZ+D3KRQdwv4A27Lw+312zs0BKZah0el26fS2SJOErX6H8ngNTdNQhUC1dV46u0isGhQdByGFWKbFxFiVKI6JU9i9Zyd3HbuLF06doTcIsC2LuR1zdLc63HnsLSwtXcSxVWpli/maza/9ws+yf3aM2w/OY+JSKVp0t3okUUiSJnS3tiiXyrQ6bZrdTcbGKpw/9xrvuvceNtZXefqZZ3n3+z+AblhEoTeUzEhIcprlS0YRiBRkaWidlOA4RbRhSEouH9MsE0M3EEky5EoFGIYxsuW2Cjaqku1AZAn8YZaAqirIsoSqaWi6msW3aRqappFHzWVUHkEezpI1HilxnPDVn6Cj+qkA0xORG+AN49S1a9O3HLwefr6sio94NfKoYmePs30x6bVR6PXktJzFnnOzcuO97P3cTeFaMrOiqEPQjxEwHwUhiqKgaSq1Wo3P/PtP8dLx41SLDrZu4EUZTiAJlQECzxPU11fouR7dThfTlFlZrdPpukyMV4hcH0XR6Q9Cuh2PwOvwT373d/j9P3oECZWFmw5Sb6yBkCmoJkVNpt1qcdPRO/nWd59mZWmJG46+k9/83X/Lgw8/zL/6D3+No8HunVPcdustKEqe0gOS22Ht8gX+4E8WOXLHHfRTmdsOHuaF506wY6LKO+88zPJGl1eW6yimiik5eH4LiRhJ00lRSSUZw1AJB17G9VIydb+qqaS+y+rqKmkqs7ZWp1S2GQx8xsbmufHgPJfOLjNwXQZel3JxAtUoce7cOVRV5bbbbuPs2bNYhs7b33UfL506SyLHnHh9Cd2yEbGLKtlImoofhSzW6yzMTnD58jL2WIl+FNL3PHZsn0NTskimOXMSx7Y58+oZpmoOppXhOXv27KHZ2KBk2yxevMDUzBSWYbJQq1KdrHF1aRk/lgnaHUoFnf27Z/m1f/xPufOdH2TQcCmWqtx+5DCvvnASd6uLF4cYBYdXXzlJz/MJXRdfqCiSzOXLS4RhyNPPPocuhfydh+/nzlsP842/+hKbS+eQRcyFtRUUQ2f1tUXkVGZ6eobXTp/BcRyuXF2hN3A5uHCQk8+/SBjHrG6sYnQ9do5V+Lf/4p/yuc8/wuKlzJLINE06W010R6dYdJBTSIZUnGJx6NQRxlhW5tNWKNi0By6aU6JQsFGVaynl1+NKaRKTJoJEZEB73lHFcYxmqIRD7CmMQ5TrcgkMwxySpzOM2TRNej1/5Gf1Zm8/HR3VZz/zqQfefz+KKiErKaqScTJEIjJzLUXKlPO6ShD5GWdKpMPYKEDEkCZZN6RIpJJCt9dDVpRhG5pzPmJkWRl1U7quEQ090bNkE4GqQEKCqmukSoqSpoSBTxKlaIrJQBPMze7gYw+9i/r5H7HNStleMpmbqFDbVsSxDTQNyhWLbVUbXU3Yu2sHk+Ml7rztECVL4sCenWiqytryFaZnxwhTUFSFoqVwZf0qR267i6efPY5mmVx87QxOsciunXtYWVoFHSTV5LHv/YBBnGLYGo/85ZcBhR07duJ1XISssb414LXL69x+x63smd3F8oXzDKI+BaeAamj0ej3aLZcg6iPrIUkQ0Et80jAgdj1cN8iIqmmMrSrohsNGrwdpQEGDomHQ8TzGyiUkEnqdJtWKRnlsnOZWl+07trPR2mQQydiGjk6InEos7J5DVQEpxfN7vO2tx0CAt+WS2lOcvbzJI3/zA9wYTlxsoFsGiYhJUYiTkFJBZ3LMYvfcFDEJU1NTNFZXmZwYZ8wp8/rZs8zObs+sT9w6uq6iaxpT27YR+CGKpLHW6OB1W+zbu4faxDhXr15lbaOONxgw6HukpBTHttHt93HDkJtvvYN/8D//cyzTRJJhvdvj7LnzHD50A+WSQ6Pdw9AS1IJMzSry5OnzFEwLwynTc7scvnEfjuTy9qM3sH1biSeefIrDN+zh5VOn2HL7TO/cRei6WNUixbECshRh6hbTU5MokoypaYBMGgumt83w8vMnmd85R61W5vs/eAlJS9l902E8N8bQNQqmSSqyXQhStuXNYJThQq+pJCIhCAN0Q0dECaauoV7nvKmqygg8l1KBOoRh1GGcXc5D1HUDUlBkBUVWEMk1gX8OpyiKQtZFxcPGQULEgq9+9c1v/X4qeFTSEDvKP0gcxyNeh6KoxGEMw25GlTNCpxCZVWw+sfB9n8HAHxHQ8k4qJ37mU7zrSWo5Uzu3EUlTiGUVIal0+z5hINA0m7GxCfbv34NhCT7zr/8FP/fAe3noPe/CVlX27jvIxNQs22ozzEzPcWjvQcbsMpqQUROZ2WoJW4opqlBSYWF7FUeH3TMV3n7XESxNplFfpTuIWevDrl37+frXvsVWq0O5aKOXqniJYKmxjFIEv+dSMnXkxEME3cyTXDOxzBKpUIlEzI65ObZtmyCNBZ//m8f4oy99Fas6QdHJPKxtRWbQafLnjz5OayvkH/zmb/P6pUXeestRHE3lH/3mbyDJMWHkEyUCL46xLB1T0zG0zN5jrd4EGJ33yckpSqaN3/cIEsHZpRXKtkMUC1653OSvnjzHxZbP6+s+m0mJ7QePMX/DnZyvh3zhmyf43KMn+PTfPM6jL54mHC9xsR+O/KikYXK2petYhk4UC7b6mSC23e6wbWqKU6+eZunKBdyBSyQ8NlqrBL7AMks4ToUg9DAtlbX6FZB85nfPsnx1iStXVigUStxzzz1MT09RKpVotzvYKnzoIx+m7cU88o2nMZ0SqaySpDJxKGj6Kmttn/p6kySKUSSTsdIEN914ZLRNCQYeBVNl4+oSbz16kJKlstVskvS7nL+whCTrqIYJEvT6Hv2tLhtrdZqNBooic/78hZG7bafT4eabj6AoMrValVbfY9fOeY7dusD5k8d56YXjmKZOOiwUObXnesXF9QRpTdNHLgvFojNy4cyJ1jmPKjOvzL7jIMiw2nwXo+v6aLqXT84z3V+G++aE69w7K9MiXrNTyu/fzO2nYuvHkFQZJ1kBSsU1byoAXZZRkBFplvmXKPHoxOYftlBwRqNPz89+5MBIapNTG3JOVByLISj+RnuZyVqNQd/j2Se/x3M/fJary0tDxnsMkiDs+8RBzPpKnWpJ4TtPPIWaQLVSQagy/c0WQRBekwykfZIkxuuH6ICIPQwZprfPYpgmzpJKuWCz3g05v7REs9lkww0Zq5RYXVnEsize/8C9VMdKBAMXd7WRYWqS4Ic/fJZ2r4FhV2h7A378/HFKlRI79yzQfuHFTJdo2fRVnW/+6BS//tEPstVpcnWtiSbH/MP/8YMcPXSIanmKY8eO0ayvcmD3HKd+/DRvvXk/z768SAzEiklnq0WaeIg4xu17mLqJrGSFPwgz5Xyv7xNEMkgqhbKO78VUxmu0t1wUyeGpV1eRzjTQNJVHHjuFQoysmwjVJkSgJTIF26HX8ShbJr7vjRQBcSwzPVEl9jMBrilkCuUSpmmyvlbntjvuIAo9lpeXmdm5g06/R78fDnHLmI2GS5JKHD58mKtrq6i6Q5h0SBDEwufSpSWEEFQqOlNTU1iG4K+//EgWjIpMhEyxVMEfeplFSchWx6XdqVMoVon9zPn0heMnMTQTPwJN9inIIbfs2YMuXC4trSLLOrZuc/lyk4npGlu9FpeWV5iqVWmurjI5XkVI1wpBpVKh0+mg6zJ79y3w3I+fpVarcmW9wfLlC+ycnuHFV87wve88xnvv/wCbS8vXBZtkC33hambpVwAAIABJREFUOpVGhjuFo2sht2y55mOeDZJ6ve5ooHStQ7pG8ckm7ProeV68hLgeP2ZIMZJHzUSWUxBiaPro+n4zt5+KjorrQhiueaLrw84qRpUzobEMSMO49SDwhkTNcDTNyO/zqPbcvyonj+YnOMegrg9s2LNnDxcvXuDnHn6Av/tLH+dPPvO/c/nMaUS/Q+K22FqrE3V8/CAmTWVOnDxNnOrousK2iSoogtfOnaEXhKSaSghs9rqsNTZpdwd4oWCt0aK95aGbNucvXKDT67B3zwKHds+yd6qCKTxaHQ/P84mCkHLR4dP/6rcZNJbpr61w8eRJVi8tcurEc6xdvsCRg3t427Gj7Nw5hWWrRLHLar3OIPApFkvMzc6yc1uF1fUGVqXKf/rKY7Q82H3gCGEEibeCt7nCZ3/v9xgvVbm0usTszlkWXz/DB955L4oMpu3Q7rns3bsHBTDyH62eSY96vS6VSoVez+XdD3+ASMrwv4JqIhKZrVYLVQY1iUlVEzQTP9YRio0oVPEAz/fQjBhhQHfQwnAgiDNJx9ZWdsFkxMN4pL+MU+j1XcbGq1SqVU6eOsXp069jWWV+9KMXqVanSBIf3ZCRlJCtdkS75bHZ7FIqVrl8uYFIVWZ3zmEWZJrNJsWiM7rIdV3lFz72ESxJUJRiJFWl43oUnBIiFNQMwc6JCvv27CFOBAVbR0pj4tBDIh75ot1800FMKeTq1VXcAEJhstnx2DY5y/KVVQZBTLlSwamUKFk2sshcUlut1kg03u97lMoOX/rSI0xM1HCKNjumqmiyzHveez9BLLNeX+Xpp59GhlGRy+GOvKPJmeHXy8+M6yZ4ma5PH2FTb9TvZY9zA4CcdZ4b82XXVTzazeRqkfxxztPKdX8/6e2nolClZFwlTTcRqYqmqyQixLJ1RBoTS4IwjYklSFWZKBLIcmZap6omqaSi6pm1iKKZQ4+c8A0rADCS3liWOVptNAHIgjvfepTP/Jt/RhmZwVqDMirEIYNAIEIwFROBwDR0fGQ2eiEXz15g/9w8JaeAplnMbJuhaOkUnWzLohkmsj2BUahBmqLJKSJNMFDQFY1Wa4tLF16hvnGFdmud2/bvZaqqIFsSnf4G77nndv76i18k8Dx+9NwJTp9dxFNUihOzBKmKWahw5eISRTnk1oUZ5h2bqYLDE9//Hs3AJbZU2p7LY1//Es2NBqGq8p3vP8tTzxzHckwCN6TZaHBldYVzFxaxbZsfv3icfTft49VTz3Hs0AKOEqMCV6+soKQyRcVBeDEi9pAUnalaFSlwmapV+eJffJmldodQ1+ltdjHGNCRNI5YkPD0FEYEIUZUQRfaJBi5qCqqkQ5zJmVTdoD8I0O0CSZJSGxsj9HoQDSjZBnqaoEkJ3qBNP0pYXLsCkkLZGmduzx7e+4EHwFJ56fVTHDx4iJdOnsb3FbZNV5jbOcXy5QuIyKc6oxLh4nk+fl9QKttcubKMJEGhUKLZ7vL4t7/FRz98PyUbbAV0TabdaaBrPkduWKC5Xmd56QILO6dwAx/bcajsnadkVrEcnTD12VG2SZIunh8iEWOYgtgQbLkNKmWHom7SrTdprKwSSILlxioJAkOWMVSViYka2yZrGKrNxHiJ224+iNdpIVvw1rvu5i8+9wjrvksUCL74yBeQKjaDNKYfhaimTjhc7HPnjFzylaZiZDCZFatwNDi6/hoxTRNTN0FVSYadUaHgjIZOWTHjDcEOaZrRfnI3lJxT7vtZJJ7MMHD4v6Vn+v8Xt5wIFkXhqG3MW1Rd1zF0G1XRUWSVVFzT3l3vJpjT//MvID9xwBC7ikfU/jCOSSUwbZ3HX3mGX/y5h5lDRo4y3VM+7UtT0BUVTZIpaDoWMjICEYekqslzr6/yyvlVBjFoumBqm4FTzEIDFKfC48+d5FvPn+IrT5/g1FKLlU2BU6nx6qtnEQOPzavLDPoenVaHjfVVJieq3H30MHrgMWaYnDu9SMGYoNf2qJVL7JqpsbzR5eJ6h7NXWnznR6fQjQqmXWKj1WRqtsZdN84zZchYIqJxZYnK5AQf+tgnOHLLHWxGMVKxysuvL3P8pUXsygTNZpP56SoFVbBz1zxjtRq7D+yj7Xu84913s7bZJVUFXuAzPlaBNMZQVQLfo6Qq+N0esqzQHURsbKVMTW3H1BXGJ4o0m53Rec+dITND/4xyYlkGeTClYWjDUXbmd+S62bh9MPAwDJOFhQXam81MVGza3P+BD/Dgwz+PH6voZRssgQL8wad/H0fVmS5VaWw2OXBwL5PTDshkC41tYtomaysrlArZVKxarlCuViiUHIQE1W01vK7A1mzWLy/yM3cf5fDCFHbisrNqs2uixOZKk6JuU3Qc6murVKsVUGzOXqrTCSDtx5iqzN3H7sAuqMiKiQjlzGs+yGyETUun3+8SBD7T0zNEUcyOHXPDrkVlMPCI45itrQ4r9WVq0zUWryxy05GDHD16lG8//j2urjax9QrRlkdJ1vnx95/GMEwcxxkVnHzbljt+DgYeqpodP8OZ1KECRH9DF5Xr+PLOLN/t9PvuaNHPca0c5/U8bzTly4tW3knl7PWcHvTfIy5rSZKkVyRJOilJ0onha1VJkr4rSdL54f3Y8HVJkqRPDwNIT0mSdPRvO3465D3lwFtOIcg/6GDgD73PRebGOtwi5krs7BhZVbcse3Si4Jp1TK4EtywTpMxc7y/+8hH++LOfplbJPII6vWz7WCqVRjllIQKjYGfBok4JWVIpFmxUGSRN5ztPnOHP/8+n+cPPPc4zz9dZvDrgy48+xVe/9QyhVCKSbTyhc7kd8uyZJb75vRNohSrFsYlsdDsIKZcc3vPud3LzTftJQw9TCrF1k42Wy4uvnqHZ6dLpdpmcnKKgKZC7QwCqrlJwSkiySrfnc3l5ifvuvJPtjs7smElzeZlyqcr5c4sc2Jtp+JRCiSsdnyeeO41qlej3utTXVokGPgXTpL5ax7BMfvj049x9535MXYYkJvA9In9AKiK2VceQ0xBVlXngIx/l1OVVenFmoZwEPvX1Frad4XQZefeax9f1+EV+y2x6MgDYdT3Gxqr0+26GXcYxW1tdZiZr2LZNu+ty4qVTPPyhj3DPz7yXtbaLPVljy3XZMT/LkcOH6bY7YBpcvLRIfa1Bs9Xi3IULVGs1FE1lrFyhYNl0Wh1UWaWz1aVaq2EVbJavLGOXS7iey+raCs2NOmVD5oPvv4998zPsna1RKMp03Cb9ICRRTHzZZvvuwzz5gxcJhoJfPxB86/Gn8cKsSESxTxJ6qCJz59ja6mBaOpoms75eH52jlZUVFEXGtm0Kw9/enhsPU52coVytURqf4F/+uz+nGapsDGJabojn+Qw6XU6dOkm1WiWKwhFumwuC89CUvBBldjA6eWxc3lFlkAhv6Igye+7su0oS8Qab8CzY9xpdKNf8Xe+gkIes5MOra5yqN3f7STqqd6RpeiRN09uGz38HeCJN073AE1yzHH4fsHf4368C//FvO7Ak58xyefSBoigefUhV1dF1E1lWR9qm3LQrn2zk/szZJO9aSMQbzOTJLg4/jPHDkK89+nUqgY6u2VCrIlvO6HiZxUxMKAs23G7m5DksTkoaoyce47aK7sRgQ1qucHK5yQtnGwSMMza+E8uoUjIcCoUSQSqjjFXZDFVeu1Tn0pUGMzsWqJQqlB2HVrPJ9596nMUri+zePc8g8Njs+mwOOrR9j0DInHj5DDUL5iccblyYRY08YhFy8dIiaaqzseGiFmusrzWYKDu8/95jjKsycd/FLuisnDnDTKWSrXqqzLovU51Z4Mpag+rkDOtLKxy+4SCba3Wmt01QNFXuf9e9DDpdtBj0YR6frsiIJHNB8EXM//YHf0RsO0imSrlso2k6jl0dYYyZ2aA88k7KtZS5a0XuUBFHmYBc10x6XW/kgTQYZKD65UuLBIFPwXG4sLiEpMHbfua9vO/BDzO/9xB9ESIZOiv1OnrBolypUHBqjFdmOXTzYe55x730fY/Xzp1jfKzK4oVFysUSErB9bpZUhrX1Bn3fpzTpMH9ggUNHj9BodSjYJpcuLdFqtUhTqEyZHHnLYd794Ad4+dwyj/7gBP/HH36RNNWxVPDTmEDIvHpulX5S4vAthzh4YIGpbSW2jdu02k0mJ2tMTU0gUkG/7w2zAEJuvPEQ5XKFJBF0Oh1c10XTHArFKntuOIIfyKxswunLDdxUZiCBH8eEccyJl1/k29/+9ug3nzsfZGnj8sgN4XqHkNwnSlHU0XWXbwnh2sKSO43kxSuO41HXl6eN593T9UEP1yfT+H7G93Jd9yfqqP7fTP0eAu4dPv488BSZj/pDwBeGgQ7PSZJUkSRpOk3Ttf/qkQTE4dCoS5aRAVlRkVOQUxDiGt6U0RLiUREKwxBFzk6gbdsjWkIuWtZ1nUESIouhnbAEjh7zK7/+CQqRiayXWG90sxOb6IRBk74EmupgWCaqHJMYgnq7w1RhBrnTJAWKlRKDTgdLNtFMnUEgkGVBGLjoho4fdIlETOBn0dVKEhJ2fQoFm4u9EK3tIb16lrmdU3R6XUQKiiZz161HePXyCsdPeWiOjaVXSJMQ2bQZd8oMPB934BHFTW46sI+tZgdJlTEtDcWw6fldhG7SWA+4vNbinrce5cSLZ9kYxKiVKmutFuNOhWYvi6c68coZ7to9y9Wls0xNL/CNrz/KwUNHaG91ObD3MI9//VG2l208AUmiEYRdCgqUlArOZJVXF1dAL1E1VTZdlyi16fVcACxVJ5R1ChaE/S49CcYVmTSV8SKBJkK0YUBsGsVIhZjAc6mUqnT6HSTJJhh0ePC+e3j5hROUKhNIssxKvYGPyNwWJJ8jNx5Fk1QsTaHTXObi6TPEsWBtrQFC0O52cNMQTQjmJ6eob9bxJNh7YA9ux6VYqdL3u1TGqhw4eJCtjsddt9/BerOBpMj88MenuOHIPCfPPcX2qRlSo8TLL59kZuEw//w/fA4kB6SMnySEQEggQoEUCK4kHpdfWuTMpRXUJOS9b7+T5XNnKJdMpnbOcPHsOWqlCvWeyx37D9FvN9i3Z4Ff+I1P89bb5wEYHzf543/3ORIFpBhKpkl1eoa+F+J6LqWCTWVygivrDaa3T3H61CkefuB+VldWkdU3kp4VheGif61H0Y1rIb+arsPQNFKkEMUCXZORE0EShXhhOHJGyEmc11N+bNsmHZpSpkMAP38vD03RFJnQj0n/O9i8pMBjkiSlwB+mafpHwOR1xacOTA4fjwJIh7c8nPQNhUqSpF8l67iYmpocxlwNOxmR4U35GFWS1dEKkdtF5HHsuq4jS9cCS3O+VDycEIVhCFJ2vDgV1CYm+MiHH8Z3Q0hiCDsUbZswikkkwba5/aRxnHmLRy5CUen1XAoFh37fY3aqRqPZJPE9VBn6gy6GUqJsmGhxSD+FJA5RZJVBEKJpWeoLsoam6fR9AAu3F1GYnEBEEqVCmVSG6vgEjfUmwaDPxHiR9a0BvpyiIJPEKaoFvcjDtm2aG62Mm2LaqKpMKicYyJTKVVquS8/3EcArZxZZ2DXPLZMTfOW7j2OrOq7bQRIgKzrdQczyWoexSglZhh1zs4SRT61W5aWXTnD06BHGxhscP3mWQI2ZMWwsRWUt8lk/u4JIBLaeGa9Zlkm/7zE+ViEIYkIMEt/FEiEHdm/jwP55mleWmduxwLnlFVptlwfuPUKglvg3f/4YhqJknl29NqoB/X6PfTu28/rZ13DKReI0wVQNHKuIhuD8+depjk1hmga33347FyfLuO15Br5gfa1O1PewLRM0ma1eB8KYgaZScCp0NltoikptcoLyeA2xoXP3sXfz/MuneOKHz7HSETzzzDMYjk2353LlK98ijmPq7QzvjAKdP//it9HMEpLiECUh8XVM7GKpgh94DBJIUdnYDDE0lc9/9SnSNGb7zATPX3iOKAi5/ZYZHv/RSc5e7rJrboa/+OZx5GKJH5xuDKGLLqVa1vG5W126UUxhOM3LM/euXl3FckyIBc31VZ555mn27j+IJqsg3mhXlHdZI0skGHVBOeieQyz5pDz3f8u3dpC9n3dZup41BkGQMdNznDnDv65dj/Hw2sp2Rf/twfS70zQ9Srat+6QkSfdc/+awe/qJ/GLekOtXqTAY+KPQzxx3yrlIWaspRgSzfDuQP9d1fZhuoY/aynzEKkmgShCEPrWJGp/4lU/Q7bs4skmlUGLM1gk9N+vkFJVLV+usNzsIhrYtqcBxHIpFhzQVvHZpiSjOBlixrGLaJp7vE/o+pqoDIVIaoiqgKdnURVVV9u2coWYI5MRDkkRG8Oz5bKy12Gy0iPyQtaurNNbrvP2td2eiWlUlCDxUxWRqaoZjx46h2DrdvkulUsXvh7g9lzgM6TWblC2TIPQIAp9SpYpTqpJKGaF0bekCH/vA/RhyjKwIygUbDJPleovJnfuobZuh02lRLNpsbbXwA5d9N+xDUWFlZRlNk6kZJuViCbNS5UrLRWgFisUCkgio1sbZPb8bQ9Vwuz0iPyDstrhprsLf/9j97J5yKMUd7rplgbLlc+dNC/zSz72b3sYSUyU4uqeKNPDpex0M2yYNBZNTNYqqTqXk4HouXq9LuVhiEMakssrr584SRj4pWYTZgf2HuenmY+w9eBS9VMIqlyiUS2zfOcd4rUYiBEmq0uuHlMwqO+b3M7VrH7fcfjc/PH6W3/qdf8H3fvgibgCvnV9Csyt4vsB0KgwCQSLpdMMsKUYydKJUHYbCuliqQCMmDV3UNMTrNUlin1TKTHeFqtKLYoReITVqLG669GKdUHZ4/PnTjM3Ms+4KnntliQvrHlbNQSnpRLrAk0IGQUyr0UGgotrOCMc1TZN6vY5t24yXKxDEpJHPiZdOYBUdgkE4WrivpxxEUTgqXNeKV45PiVERSpJ4uF27po/N7V1yF9BUyIRBjKbqSFzzgssKVwbHtFottra6hGE43C6GP1HJeFOFKk3Tq8P7BvAV4A5gXZKkaYDhfWP4x0cBpMPb9eGk/7Xjj05C1kXJoyleXsmvZ5XnY9H8y8oN9XK8I+fDjPbgQ17MV7/xVTY7LQQxiS+IfQ8vCAkSEIqOGwr0gkplW4kwFIxXp+h0ssmV62b5d6Fi0g0FbgDrXY9eKIhSmVQxMawSBUvH0HUCbxgNr9lYmowedtg7rnJodw1NE3QDH1dSKVgltlpdNtYbDPouhw7u5/VzZ9EVeQh0QhQKLl5c5DuPPYqsyzjD6CKQ0TSTra0t5nfO0VhbJSKbwlSLFcQgBCUrtG67y9WL5ziwbwHD1Al8j61eF81yeOHkWVavNiiVM1fQcjljZxuGSrvdIhEhURQyiU1jq8ezr1/EUsYh7KKnHqEfEsaC8+fOMVauZGkzxRIP3r2PfdtU0u4qB3ZnU61Gu8XMrnn8KOTVk89RmJrjwtIS7zxQ5Zc/8WEse5gupDhsbDQYL5fotVrIqsx4weHq6gqRDP2hQiGPaEqJMWSTMWeCu+++l7vuOMYttx6lWK2weGUZFJjbNU+agmU7RAOVO+64lz/9/Jf55G/+M8zaDHp5gk4nRJdt+q0mUuChiRhbBmsYq6brOrEQxKKJqcVEA5eybaJGHlVLpmqpyEGX6XEHLQ1RRIhlyvhpF9UAkfgQxkiqjdsL8XyBUEw63SZh7KMXHWJFxe8K5NjEUipIUbaYOpqKrcjAtXzK9fU62jBV2uu5uN0uvXaLbq9LIglkKR8mqSP8FzJNYO5wmycw5brXPLQ3d/9MU0bGknk3lfu5Zc+zEJIkyUpKHuibJ8/kce/5NatqMrrxk6FObybSvSBJUjF/DLwbOM0bg0b/8wDSXxxO/+4Etv4f8ansuEiKTCxiBIIoEaSSzCAICa5LOvb9jBWsayakMoqsDuPDsymepGSi2TCOSVKBVbBRNJWeF7NtZoI/+49/gB1BRdIpFUy8ANo+bMWCrcCjaJuYyKyurBCqcHmjiYhN3K7A98AfxEipytbAZyCBrJi4yESodNpder7LkcOHSVNBIGJCSSACl8nJCokkCFITpe+hCvCjmLPnl1Ana0iayfTMLDfddYyNnouuwt13HsEfdCnaDl7sEYQh89v3I/yQVIdYCQmES6O7QmLDYr2BVawRuB5Hbj7Ei6+f4UoQcrHd4fi5JarbZyH1GR+v0vWy402UqwSp4EKrQUeo9FOT+X0HabaaTM5O4aPzkY/+Mv2+TMWw8YBGx8e0KiR4IARdL2T7zBRGEjM1UWOj2SEa+Ny82+aeOw9x8KaDnFte5cLiMpWxGmFfcOKZ40ihTzQI6aysogQhm60uV04ep6hDLHxSXWCYDt3QxTRt5ifn8SOQVRNNkikqMhfPXsC29OHEycSLPBRdMDU5w4MPfITqthnGahNUp6bo9GK8IMZ0SswvHOT08gr/8J98imKphjXm0GqvIishqeJjFFTcxCeUBbIqEwx8UlkmSAWm6aAGgv37FpifqHDPoQXesneK+24/wid+7gPMjun85t/7OB+//x5+/X+4nwfffhtpp0nqgxLJKJpOoAskXyDrJrEsiBIfSdLZvmMe1/MwHZtUFRSrNjE+SD6GZWJWK3iJyMT3qoof+Si6ytj4FN1uh02viztwoR9z6cw5vv+972EXdRRVRZLl0b2q6Yg0MwNQ1MzKWiSZjUscZVbXSRwThSH2ULycUUyyqXvOQcybi1T4pMJHJD4SId1OB01RiQIfhEBWyQwpAw9Dya5f07B/oi3Ym+moJoFnJEl6GTgOfDNN028D/xp4lyRJ54H7hs8BvgUsAheAPwb+3t/2D2TeTvKoNQVGUwjtOqp9zqi9lgqT4VW5Etv3r6Ue5+6FSSLYPT/D//q//DaOaYKAaiGbfEWyoFC0sQwdKYpxtzqYusr4WAZg26ZJGsUQx4gwzB4nLpYmZ9s/VUX0OpQtgVWE9UGL5188i2VVURUTCRVDgv179uAHMVc6LSJZpVAwKdo2/UHMar3FzMwMqqxz7swZ9ERmW6VKKgtUQx4m7GYr3draKo/81dfZffAQ2+ZnuPWeO/j4L/8a73jP/Rz7mbtZa6+yutHA7XezANXIQ5EcymM1rtTrJMNkYWQVw7aJ+y2S2KM9iHnyxbP0Byqr9S7j4xNomsod7/0w9z70CYQio1sqr2008VMIey32bLeZnyihJDGtdotWu0Oj7WFocMuBGd7/9mN87Wtf5+TJk4yPV5mdneXChQsUCg4TExO4rkttskZ/4LNWbzI1M4eUQhyGWKZJHGROGotLKzilCivLy2iGnm3rFZVo4NPvu9i2fY2kaGRW0IqmYlgm23fMs/eGg8ztXMAYBnIu7NvHX37jq7jECE3OnC5TGUPIFFWdgqzTb7aomlWED6ZdQqgybtDFMQVW2uRv/tOneeCuI0yYMgaCer3O1eUlTp98EV2ROf70M7z8wousLC7xwffdz598+vf4lZ9/gIouUEUMAgxFRoisO5moTmAYOmtrq0OXA4eqU2Kz3iIIQNErBEKmvdUhESFSEpK4LgVFpqybdNcbBIlMwakyOTFFu5fp9y6du0C1Wn3DNi7HiYAR5Wdrq/uGMFHf90cUgrxJyOk++bQ2pw7ltKB8upcnmOfgfM6K931/+F3J15qOn0BC87f2X2maLgI3/xde3wR+5r/wegp88k3/HzDsqIYC5Jx6kO+pgVHxya0n8lDRNBUMBmEWbx74OI4zZMW6FIsOQRByww37+Me/8au8/OxzKLLJxmYXV88CL8sVhyCOSXshSgpjY5VM+KzJWIZJc9NH0lSEJBMLgayqKLpN3w1RZJk0cnnPPbdRMWRm5uf5l3/4ZeTURo0hHAY3DqKYq8srWJbDSnMFfxAToiKjIskqFy+tsHHJY7JmU5msMr//MC+dOs6dd7+T5144y2Y/RMghupLZMW82OvzWb/02Tz71GCKJubi4xJPff5IDuxf42Y99lL9+5BFW66u86967+e5TzxBHLgITy6iQIPPyq2dQjAqJkNGBQSywCxUM1eQrTzyLkvh88pc/zvj0PO97/0fZN1PLIs9rU0ibXUoCpidrWFLI7K5ZTvgeETo33LifF06dYbqssmvS4eknn2LXrnkcx6HRyKKpZmZmhuP9jN28ulbnwMHDvPzKOY4/fxLbcvi/qXv7KLnO+s7zc99v3br10tXV1aVWq9Vqya1Xy5Ytv+AX2RiwgRjIJkAgkwHyNpMTIAuZ2R02ZzYhWWAy2SyTMIFJCEtCyASWZRJICC8OIeAYsGVbNrIsy3qz1Gp1V3dXV9fLrVv35bnP3T9uVUn8swNnc/aQe46Oj3Sk0+3qe5/7e/l+P18lTYmDAbVqndXNTfqorG60uGH3AqcuXEQzdaI4Q8vEsaDf98f3CUKCAmLIHz9801FWrl7CHwTs3BPQWm8wtX2GVDWRCli2jSplJuNNJOEgGILqGviJj2npDPptNEPlxhvmqZVN3vTwg3zgN36NomMiI0GjvYJumig5h8ZmF9cpIpOIIIkIwiYf+M1fY2qqTqlS5O4j+3jy5GmMFCwj49zruklnvUmUChzXBZEhcogEkxMVut02SJ9czsTSIhxbcstNBziwf57peoVnTpzk0W8dxygv8NJqm5VBQCEPSOh3ukPoY4Bl2WPpwOg5iuNMVB0EAUJcwyJdH3IyzspMr8mHgHErOTqwRpvFJBEk8vvzC2IRYJg2URCQzzko2kit/s9MmX79tzvSRo2CEkb6m8yYKsY6jxFtYXSaj8yrI/3HYEjifNnL7uIfH/0GeirZXMsemJhMYxV6XTq9rIoqDdNfExGRxBGDQUCKIEISpiIzHMcB0nJRLReZCO678zA3LMzib67QunyeN7zyLhJVohhg53RuvuUQMmeyudVmqlxDQ6cvhu72FCzbpTpVp1KpsnN2jpfdcScnz57h4I2HOP3sSfZsm6FQKI5b3vn5eT7W64etAAAgAElEQVTwG/+e6YkaP/GGn2bvwmEUW2XxxgOcfWmZ9VbAHXffRRwHPPrNb3Bo7yLbJk32ztfJGyZTtTqqqmPaNvl8kTgCW3eJ/Ii4F0CxQjtR+c//5+f4hV/5LW7Zu0jq+UwWijzzvbOUTJWH7r6LW2/YR9m2iTrrpHEEqsrJZ06ys1bhPb/0Niolm422x+ZmiyiK2Llznl6vS6vV4sCBA0O9ToSVs3n8yeOIRPIv/uU7SJKYoutiaDoba+u4bp4wgVRROX/uDIVikenpOo7jkISCra0WW1ut8YOiDHlMYSzwfB/LdqjVZti5cwG97HL03ntottpZiK2iozOUsaSCfMkl1VTWW03yJZdAjShUHOLY41//3Fu46YZ5CqrkM5/6M3q+YKMjQXOoT8+wMDfDc+eX0PMVXlptUt02z/ziHg7fcjPFyTKaqdLptLl87jQ//tAx3vTgXXS3mpk9TAhcO1OMZx1AxopSLRt/0MJRffbPV5g0Ax68Yx9vf/hOjmx3OPP44ySbLW67YQ+f+N3foW4LbAV000SzTLa2WqxeXeGx735nXADE141Rut0uvZ5HGEbjzd74wCf7PH3fH1dg15uafd8fizxHQ/U0ZXyojbyZI3HpKChiVFGNAk35IQSfPxI8qo997KPv/4mf/PExrjSV6ZgICCBJUVRQNQ1FzZjmo0vXNRKZjE2TWRioSnEyz5ve9BOkiSRu+5iWi6JqlAp5RByhWxZBlGCmGpqqo+h6hi+2cnh+TD8QhHFKGIUYuoFhaii6Qui1QXR56J4DHJqfZnnlMlEq0IsOg2jA+UsNUlVDxD4zZZNBHHC5scYbH3qIpdUlAl9guy49v49TdKiUXI4cmCMebHHu3Hnm5+dYvnoFx3G59aYjfPmb36VQmaLbaeNoCY2NNd75y++m7w2YqEyxcMMiKyurWLrOdx79BxqNBlMzOzh85FYma1ViX0FTJIbt8OgL5wljnYl8lTSMCPw+qZJQqOQJI48gDMg52ZC17Li0N5aw8hYrm5tIJLtqk7x47gwXV5ZodvokYZ//8Z2/yomnnmbh0D6WL1yApM/Fcy8wNVllftcMl156iXarw+z2HYg0oTMIsQyLwOtiGBYHDxzkypVlvv5332LnwgJzFZet5iaRlqfvtSGn0e/0ObR4hPXGEh2vR6pmOrb6VI3Fxb04rkMiQwoTFaIwxNZN8kNxqa7pTE3VKbqZPeb0mTPoeo7WRhPTMFAVHU0x6AUtDM1ChAoyGqBpIWXX5Jd//md46rFv4rW6eEHIaqtDcSLzbra7LYq1SZTiBBdWeqy22gjN5MzSMhvtHl/+h6e4+fDN9FpNJkoFDMtiMBiw9NJLzG+v09raQjccEpHFxTmWiQgjDEUnrwe87cdexsEdBW47OMcNMxVKtkXeKeIHEk2XVCoTPPnUk3R7m8zM1LmwdAUjX8TrhViGgZPT0XM5Dh+6lQzzHYOSEA4y8XOaSsJwgKoqqKoyPIyU4Sgm40+FYUguZ4/xL5BiGQaGnhFEZZLNoEQSIxJBSoo2/HfGkF2FCrqmQQoKaYaDjkL+5ktf4V3vevc/HxTxH/zBH7z/xx5+DZZlASCHyOCRjkOmyVgHoigK6jCpeIwfvo4oOBgMUNSExx77Bn/zV39DrTiJgkRRVWIhMEyLKI5IZMIgCDB1i3zexfM8gjDEtqDr9RiIhFgxkSikJJSLDoPOBm9/1a38wk+9gX5nlc2tBomAudlZ/IHg5lvv5fkzS8SDDq9+1TG2b6/yuodeybmXnucV99zFIOqBFrPV6ZEIHUWxSfwQW4YEPY98YYKFhUXW19eYmqpw4pknWV7rolkWMpFMFBwsO8dGc5O9+/dj2TlUXeXY/fcRDgaAQrPV5+KFJV44dZaL5y9zbvUqq+sNVrbaaJZGIhWCOGYgBqTCRzcsklBSdVyUQZOHX34nL7/zEK+85zBXVlao12dYaayxc7qO5weYhs5EsUS1XKRWLvOVb/4jV5td1tYbEMMDx27loVfcy/OnTuEHKZcvr/PuX3k3Txx/iiDos3f/ImdffIGC47K20eTS0gp79h5GUTXWGhtM16ZoDyKuttq4pTJe38dQDdq9Hj/55h/n8oWXiKOQJIkxLItqrcbefQdQNSNjoudykKaoiopIJaZlkkiJU57AsAwa62vYTp68bbHRzKK2gmiAEDq6ApWixmsfvIf6tMWkbRN3+szO7uTFc+fZNrsdmYI/EEhFoVguYdsWra0uW34/Az0qBgoWzX4fy84R9LvcfGAfKQqrjXWiWFKemKSsDTi09wbOnD2Pmq8QeX3QNdLUp+Ck3Lk4xU27t9HbbJDGCaEY0Ou1ieKAMOrT9wZICbpukEpASZmcrPLsc8+jmBakglq1zOK+vdx+x5202y2CIKTf94eMqcEYGZzP54d2FgXbtsZhJ6P5b9YOjvILwBiio0eFxSg0RSZJFqoiYkaR7pqmkCrKEPOtEUcxiqogZfLP76D66Ef/4P2ve/3D4x5YHXKWM565iaKq4/YHsg8q8xllH2xKVnZmeNWU6pTLb/3W+ylZeVShEMYxlp0jjCJ0wyRNDQYDQZpqiDgmjCI03SQIQ3pRTKLoiERgGSqGrfPed/5rVi6e4lV334yhBVy8cpFipcZUfZ4du/fjDUIsN8/ply5y4sRJPvK7H6TkWmyb28lNBw9zaukiIhI0NpukMqHd7TMYhESxJO53qOQU9u6eY3JqijNnnmPPDbu4eOElqpPbsPI5Xrq6hmHkkCIkTSXPPPscv/yudzMY8n38KOC2227n6spVVEsn8DxEGIJIKJccbj60iBiW+DIWw61PJgZTFR3h9zm0p8p7fvlNBF6TtZUruHmLt73jHXz043+KaecpaZIwtYijCL/XIY1DZBTxprf9LOevXsXretiqThJu0d1qUJ4o8eqHX0ff7/Cdxx9jZnsNJdVpdTaYm52j2+pzw6FFVtfbnD17ic0tn231abqdFpPbZri8sUk/FBTcIiJNiUm5dOk8WhTjOjncskskBLm8y4FDNxJFCVYue9NrqgapJNVUojhC1TRyRgHbMHFsi25rixRBuTLByuoqkYgpunm8XpM08JnMm3T6PoNNj9ZGi3NXltm3/wCt9iZCwlbHRzENdBV63R45p0ASCXZu34Zrmwy8FrMzU/Q7PZASRaYsX1miWCzT7XlIqWBZKlaasryyQV/LyKFxqlDSQ/7tL76VcOsqUb/NwBcYuQJxHCBEyv59B2lttrMDKk0pFApImbKytkIqAhqtLk6lhpIKlFQQBQPmdu9G0zR8f4BpWpiGhjoMeLiWg5kOq6rsUErTdCjxURAiRtM04jjGMAzioTo9C1NRIEkgzbIMkjjGsgwcJ0dmPk9A1TLLVZJkA3QlKzj++oc4qH4kZlQojMtLy8o2dqM+dyROGyEpRtqMkSPf9/2xS3zkOXri+ONcuHCWcBAQ+1nwYhBGeP2Avh8gUxM7VwTFzDLMUhiEAaqu46cmWi7zqx3ct4BrqnzmU5/gyL4FagWdys5Ztu3eg1Wo8YH//c/5nf/0cf70M39JuVbley8+y2++/99iGYKJcpGp2hwClX/zv/walxoNjtx+J5VKDV3XsXM6qiYwDJWfessbabXWOXf+DHsX52msLDM9NUMqdMK+h65mP1zf98nlHCYnKzz55PFMtJeAquh0PY+3vOWnuWHvHurb67iuw1Stwv75WTqNFYoGVJwilqLj6CYameIfYOeOOju3F/nuY19FCo/FPQs88cRT/OO3vkkYwX/40G+DEOi6Sc5x2DEzQxxGFByHj/2XP2S50aBcKg0tEiqdTotWq8mFS+eJZMBEtcjK+hKHDt2IrutcuHgWgJPfO5V9FpaLbTmsrzWpVCpcunyRwSDI2px+gKqbGDmHVrfLkcM30261iIOAQtEdzi8FrlsEGIuA0zSTrUQiS5q2FBPXclhcWOCGXQvkCy5+4LFr9wKGZSLTgH/z3l9i3+IMO2fmWFlpo6o2SaoSK5KvPfJ1tjpdhJS4xWyepRo6USQIBhGWomIgaK1fYvu0Q0mNqLo2lm6iGDaKqrPV7qLpJoMgYCsCzXTYMTtD4HcJpECzTebrNdpLl9hsNYkigZFzCRIVQ3fQVJtTp87Q3OjiOC5HjhxlaWmZVqvNG97wMA8/9ACWBs3mOj2vy0TRRQrJysoKnU57DK27ln7M2G8JjPVUI8ZUtvWT4+dwtM0bzaVGm700kWhKFlRqmxkAM4wCRBKhaoz/3vVJ56Pn+Ae9fiQOKoXsJM8EZyqSbM6mKSqqlCiaShhHJKkEFVJNxciZSBV0OzvQBv2AwPOYmS7z4d/5EI7ImFADM8IxdFIpqVarWTCmKQnCLikRmmVnwsFEoOom//JfvJH2yjK37qrz+nsO89Ad8zx87ABTJZ3y9Bxx6LDZkvxvH/4k2oSDVqqi2GUOHbiT//V/+nV0x2YzknjY7Nl7gPruRVRf5V3v/lVyeZf6whyq6aBrNq5j4hQr/PGffZa1rscdd93OpasN0Gxsx2Rt4zw3Lc5TsE3SWKJqNjqSXrvJv/93/zOlnE2qSFQFCCWxF/Hqh1/PnpsOkCs7VKcrrDbWkapNJKCYN4mSgPV2k0RCqjukwud19x2Gvke1UEOJ4cK587ziFQ/w3IUVahM2//E33sdq16MfrtPptrl8dYXJepXXvvUtWXS4lWe11WZdSh470+DGu1/L23/+X3H29GnigcfctjlKdp2nnn4cLVbRpU4sfGzbpdvxCEIPr98lJqDRbLK+6VGszOAPPHJuNoiNw4hC3uHbJ45Tn5vD83y21psEvo/tmPQGPnEoiCJBnEjQ9czX6TiIKGIrbhGqEi1X5K4HXsmuhX3M7tyDLwKO3XsnR48c5guf+0sIdZ574QyuIvEGXQ7eehhVSOr1GVobXVzbobvZwBagxFByXaxUBeHRa61TyjmkoaTZbDJbdqlYKt12C800URXQU0HJNNmWh42NFV68sIRl6BiawOxHXFhbJ0xhfsccCZJuax099Oj3PNJEUi6WmZ2Zoee1+OY3v87NNx/mppsO86k//QvOvrSEbdqYqk5lps6RGw+zfbLCs88+jmXqyESQd0wCIdDNTGZg6jqOaZPPOai6imqaaIaKRI51jeh6llcQBiAhCH1kKkiR2WFk6cRINNtEKBJNNdE1EwWVRIBjmli6juPYqDqgg2Fn8XU/6PUjgSLOlOnqmC9l2yZRmOFKITuwlJETO81av37fx9B1pBiWrkJScEw+/B8/QH+zjZ2zEbHAMW1WN1tZ7HSqoqsmiAgdSRRHbIUBpgJTk0U22+t8/S/+kLc+dDMHFmfZ3DjN9tkaV5YbzNxwiIG0+eyXPksQBlSma+OI7L03HMAbSBSrTBAF3PGy+8nni4hYEAkoV6pomsrMjjmef+EkF84v872nniUJBZ3AY2q+xqGDBzh+/HEsx6VUsjl39iwz22YoT7ikgy4DX8UsV2j7EW7JpVh02WhexCjMEAmJY1kkiaCcL/LgKx5k5dxFrpy7gCIlnufhui5+JEiGK1YFSV6VHDlygHMvnII4YMdcnUKxjFuKuHJlmedPnmBuxyyDrk+uWCGIPUrlMmGvTRgG/JePf5KXGl2MQoHqdB3P9zA1eOrxx9m4cJIbbzzEamOZ5547ha7ZLOyeY/nyUrbRDQIiCSQSJDi2zY7JCTwpWe4IhGhTMh1sBeJBl3zOZv/sLO1OQGOzi+IU0U0bISW+5+G4Lvl8tj27fpk00ueJMML3fUquS6fd5t57j9Hrd9FNlY3lZao5h1bexe97hIGg5DokieTZEyfYsW2G1aUGU9UqWhAxX6sxiCRp2CVOJEkaoA3ZaSPrVm8QsSYazGyfY2OzRRz6zEzX0BWJt9VGlEycfBHdNhlIlTQRmKokiCUdX7DVWGH7ZBmjXCZOJdtnZwiDiEYjS0eerBV5w+sf4NOf/gtA5ZZbbqHr+SweOMzq06dot9ucPn0a07SpFvZkHUd8TZU+6Hvkc072bOk64SidaeTfG+qdlHSIEx5WXymSXM4ZW9pGc+QRaVdVVTQlIzLACH0cMDFRGTtIhBBo6g8X6f6jUVEp15jLtp2JG0eyg5ELWx9SFSzDJBwE2KaJoenDCB/JVLXC5z7z55w5+Swyycy2UzPzdLsRTqmIadnomoma6vRDkXGwUxWGTKuF2Rne+NpjvP7BY2zfVma9uc7snpsJVJerXZWP/MkX+NR//RJSs4liFde2aa81+dAHf503/9RPcHFpCatQ4e77HkAMY+ljJHGsZpsdqSOEyv5DN1ObLnPXXUfJWyalqQqNRosTz5xislxh78EDoMLu3QtoEr739HEmSyamJgmlJIgF7XbAhfNL/OLP/hLVUjmrKKMAw7bp93zmdy7gBwGqaX4fdnl5dR2GyA9dAfwmOUWgI8k5DleuLHPp0hKVSoVyqYLXCdixvc7GRpO25w8h/1mrXavVqNXr2I7JIIjYaLaI4wDfh1fcf4yCleO7332C06dPs2vXPEkiuXx5iVzOGYcEuDmHbfU6O2dnMfVMgqI7RRzHpJB3GAwi0ijgF9/6RvbPVlm6cAkRBNi2yeZmi1a3i+u69HtdFORYrDjC3Y7kKiPkT6GQLU1GgsWXv/wB0hRuvfUoRtElUWDP4iLb65loEkXF931WrixjGDrtdotEClqtFmYq0NOIgq2iCJ+pqSogx+iT0nCEcWVlCcc0MXRoNRuEvsd0rUKMyiASqGpGDjW1rJJRTZNLy2221eexNZuB75PqcO7cWTaa60iZCUI3N1s88sgjzM7Osm/fPvx+l4tLyzz2xFNDkaUYt24jHdWI2BmGmfhyZEfb7LRJhxjvJBaISKApKsjs2csQ39G47RuB+JJEjEcyI9hfFhrhIIQEVKIo8++OpA7ZQkxHVf4Ztn6jofloBXo9uTNJxJiVniaSNJE4tk3g++iqimXo5HI2733ve7h65RLN9QaG5SJ1l4tXGgSJyiCO2Gy3iaLMvxSn2SEl1Sx+y3Vd9iwskLehWquxe+8+vvz3x/nIH3+Oz/zV13nu/ApSLxMGmUCuVqtRLRc59fRjPH/6BI21JV750ANsm5nByRexbDsTtSmQpBBGghSyt/8Q09L32shU0u179PyAd7z954jigG/+46OsrjYQYURro4lMInbvnMUwVOIUeoOAvh+hqS7bpvfw5OPHM4a1pjOIAkwt8wdOVmskQ8piqVSmXq8jJDCUcYSBz8HFBQq2jmnoTE3V2L//AINBwHMnTxPHklJJ58K5s+SdzFsohBjrblqtFv2+T5KqTFZr1KbrCBGxuDjLdx97DETMrbceYfv2GZaXlykWi0xMlImiiGq1SqlUJE0knVabq8vL5G0bPW9zZaVBmkLY87AMk6Lj8Pdf/RKtxjKliSre0KxtqpJEZA9wY3UZQ83y5kaE2AzElz1go9nIyEQ70tnpus7999+fkS9zJtvm5zLDbJA9lP2+h2PZREFE12vjFl3a3TaoEtMAGQXYhkoS+Wx1WkQiQtUhX3Cwh4jevOug6VAulTF0nWAQZNWdVLFyDnEYYalynAMgUvAHERsbTQb9jG6aKlAqu6gqOPlsVJHLZRTPzc0mKysr6LrO5mYL3TAplcrcdtvtvP3t7xg7NMIwGMLworHbY8Q+t6xhUEoiMTQdFYiCAF1Vs1/DeZYx7HBGh9XIKTLKOxj9CoMoM/RLMK9zloxU7CMP7w/T+v2IHFSgqiMP0bDF01QUQyc1dFJNRSDRbZM4lfhRhJV36PQ9TMfhox/9MLOTgudPnaQfuXQGGUNHRhFpKLBUh1RIBoGH129TK6nkwnX+7bvehpm3GSiS3/v05/D1GUS5zK9+8A/ZTMs0eoJ+KLBMFa+9TKJ4TDoq0xM2r33NK/nK17/MZLnGG9/6NoTmYOWLmcVBvWbiVDVBIjN0TZoCQseyXRpr68ggYDJn40xU+PXf+T02tiJ2TFXZNTdLGAaEImB62ww7ttUoWCq2mrU1oYyIhcee+Srv/7VfxU1UDu7aw6HFRfbv28N//aOP89x3H8dOUmo75mj5ESfPLaNp2dfutj0qpsrNC3UCr4mZs+kFHs3NFnv37cNxHZaWlzB0B4GOXXDIaSrFchVFzyoA23A4c36ZfhzQbDfxNpvctGuB973zHSiaT1NEXLp4loEXMDNdZ2Ntma2eRygj2t02QphsNLv4iY5ZKNL11vFDj41umwQoFV1IPfImdFIwnBqb3TYRGbqnZNrkTJVS0aY/8DDtDDesmzoSiWboGJpJPueABF1maUaWmU07bMfEcRz27z1Axw8oGA6qDnpeJV92qE0VswMzUdEdh0SoLG806YY+uqbSb3uYUkWPBZXJMrEMME2dRFF56uIllhWdC+seq+sBl9ZaXGmsEwrI2S6eF6D2h+ZkTcePgVSiGjbClzyztITMuziVbEFgqTZra216kSRSdKSamXsHUYTpOBh5m9vufhAjVyMUOlfaTb7z9a/zJ5/4OJvNFTQN+qEPuoomVfQ060qSRKBoKkaqYihDHSJkz56mkqSSVBmywoYIGMO6RjAZx7kPCSejrX0kIzRLBV2imiqm7aKbDqmio+r2OGXqh1Gm/0jMqFCUobzAGWMnpARVlahqJvUftYRJksn/gyDAMFQ++KH3k3ZaxFFEpw+e0AmFwETFNMDQoddqEAtJyXU4fOc+bNHm4N2vJGcK+jJia32dQqnM337rcdr9NrpbZa3RZGabyfve+z5s2+T2O4+y1WuzvLoCqU4UQn16lnK1ih8Ntybyeh9VNNySZErjER7ZzNnMbJ+lcekiR/ffwhe+8gixVJndM8erfuxhzp06ngEB44Dpeg3N0FldW+EVdx/lW0+eZmUAqCZ51+Fvv/ZVyrU6v/xL78LvddENhfL2KstLywSBipKqbARNhMiGmkKRdDca1AsOt+1Z4Ny5s9Tqdby+R3W6RhAKNNPk4I03M1mr09j6C5aWlokjSRyBQGBrIG2TmW1zvPuhB/mLv3mERrONliqsXr3I3z3y1yzesIfa1AxXLi0RhQ26nYDdCwdY21xGyhQnX+LypQaDyKax1WD7jjr5iSIT1VnMtQgtDuj1umyfLCOFQFX1rJqNBJbtkJCZut1IEgeCC5eXeLhYJAp8RonZMIzyGlZUiqKPN1qKAqpuIpKsQnzlg6/mi//3Z5mcrnPuuRNMlap0LvtYThG/G6AqAmmqVMsVksin32ziVCo4ZsaO7wWCqYkyhu5y4sWLqHaZDS/j6idKhKFKbty7B0REt9VifucMOl1a/YBQCBI1QMYCJzeKroL9+w/QeP44cRRRzLnUpiqEUmdu+x5aay00K2up5nctcM+x+7nnVb+A4eoEqortqEwXKxTyJoOgCyKzB9kWxFKgKWKM5o7jUbLPtSTxbOMux7kD5tD1McYYa+bYwzdSnjebTQzjWvpNr5fNRVU1m3HZto2u2+N2caxO/wGvH4mDSoHh7GI0X7DHN9UIFzH6QLP/gm3rGKbK0pWL7MxVOfPCRVqbHvliDUv1Cb0W9bLLfXcepVpzCCPB/MI8z3zvJCWjgmXqfPSPPsHC1AxhIcLJOUyUbJYun+fDv/shZus1LEvw0rnzxEJw/Omn+ONP/hl7bzzMe97zq+iaS3urS6qbROJaCOrIimAYWa5gHIsxGzxNoet1uenoLZx97lmWry7j2CZKqcK5qyt89nN/iZ16zGyr4dg6edem2/Ey0kCnycK0i7/UpOUJ2ggMzWGj5dHtL0MSgZJwtbWOrtmkqk4gJPHQHhGFPimSsqXTazQ58Jq7WL0aIVPJ/fffz9VGg37fZ+nKChsnz7C60SRBH5b4Ok7OZhAGiIEHqWBzbZlvfuoUV7sRTrFMfapOd6XL3PYFLr54ggsvXmRh9zy6nt3cV5YvMje3g9X1dc5fWKLdiZgoqLzm1cfodNqcPHGKzZ7C2mabRDOxTZui4zCZy9FcWkbqKigqIoV4EJFzywRhwNpGEyNfRE2yz3pkFxklrsAoly5rA0dWK0XTIYVCySGOI2bm50mTgCjwCdpdSpUqjStNcrqNIiV5zaTTarK4ex69WsbrtShOFDOoYs5l4Eesd5Yo50w6UjBV1KnXZrnw4hluOrgPKxFs9drYOrQ2G1RKDnFqoxoOKio5xyEMIgwjq1aef/4UL1tcZK3RYCAkhqISKxEvnj5Br+Nx7OV3cfb8RQzT5lOf+lOcoo5TnWVCM+nJLkcOH2bvbJ1Wq4vf6dJsrFMq1zFzNpaVvfxTqaLrEjH06SlKduiMDqWsGLg2khlF2GnqNcwwMDbOjzy6lmVTKLjj53sUApyhYOTQWyj+f0MR/5NdaZqOjcej+dTIPjPqaa//s3To6frgf/h1rl69RKz4BIGkkHdJRcCBuSqz2xdZqJcRXhsnMdlqtjh+pYmIYWLW4atf/SboLp2VJtNTNW4/cpjXv+4BttpLRGGbtWWfg4f34QceItB508Nv5P5Xv4We75MIySDMBtu2mcHxzGE5PDKEe17m7M9mD6PDNgJNZW5uDttxMCKVQqHIsucT+T4nn1/i0x97P8vLF+n3WixfuZTN6hJBwVLZOV3Fybv8/ZNnSVIQsUQzVHqDgDDx0UyFml5lEAT0fR/DMNm+a47LF19iarLC6tISpq3yjp95PUGcIV8UTeXy5SXq22dYXKxQnZ7ludNnuHeiysc/8cnsJrSzIXShUGTb9jrJoEVlsohcbmFZOv2+x6X+JcpqnmigcOyeYyiKyrceewQFFcPIU5kssby0jKqr7N9/gDR1uXrxWS6fP0WvH7FvYS/OjkXOrn2Vyak6vV6XaOBzaXWNVNPp9Xwq9QqhkLQ6HoZl45SLtNtddqs6Ydsn4prX0/d9Cvkitn3tQbuWgiJIyXAnsZDEQnL09jv57mPfQLdM7JyDkJDLuziayfRkjbDjs+EJcgWbiV1TzLsAACAASURBVHyVuxfv5PK5M3QDnyDONmGmatJsNanvqFMpQmezyfyEi3dlmdDQcXM209UKXr9LwS3zradPoeeqKHGEEBGm6SBlhGHozM3N8cILp7Atm1S3GXhdJuoVnJzKRFHnxRfPcPjmWyiVK5i2w5e/c56Vjg9pgNQDvvudpwh2zxMGginXHRMRpKrS97sYmksQRBimHPtmM+abOV5uwZDiiRwD9SzLRMTXwhxGUMtrBIXsWc3nnTFTjlQFrs20RouzH+b6kTiogDEmQh1u95SUsTxhBPcfDUMHQcBLq2c5f/kiwtdZibrM7Zin07jE/oUZbjqwh9qEw/Nnz/CTb/5pfu5d78GdmKXd9bE0He3sCkHo0PebvPn1r+WZ4yewAo9Bp0l1ss65C+d56eoSVmWGh3/mV7iytMJqK8C21bF4LUkkBXf4w0glytCBr6ZZb29oJiLKkj8yqoMkn3OJ44C+6lKbWeD86uMkYcRETkcvzBJstvntj32CQzNVbEMS6zpYOtWZOl3PxwsDqq7DWx++i0efOEmnF9CXJoo0cRQHTUjWWuu4bkaFEKlkY2UF20jZNVejWPDZ5rowaOMz/L6AdrvNvoMHOHn6DJX1Bv/wzceoTi1k7avlIBMVmQq2Om38Xov9O2fZsX0Pbz9wJ5/72tfZ7EdUci5LV5f42hOPc/dgDi2JeNm997K+3uSZp0+SCMntt9zFufOniQYt/P46xYpLZaLKxMwCX3n0CU488yWkAoPmOhIVJQ25/cZ9XF1eISnpJCJi0PfYVqvS6/nowmSiXsUo6Dxz/jg3HTqavb2FxDJMpBSEoRinqwDjzVcy8JGJxDZMTM1ECp2cWcQxqrjbbTYbLXbM6BiazuVLDU6eWSJfKfPSd89Qdhw++d8e5cihedrNLvfdfQ//7WuPsG/3PLceu4sXTp7gpb6NQ4TrOMQCVE1i5lRiFZpd+M4LZ+nrDpHIHBApIESEq0W87MgB9LCJk3OyFPGcTqEwSxwL6rUZ0hRqMzN859uPEisqj/zdGQZ5FwUbO28SonNg/wIz1SLianYQL19d4sZbb8naPKkjFIFugmHapNdBJkejl9HwXMpsTjUSdg56HoqmkySgmTraEBGeIV4iisUikRDEIhvboKgYw1nWKA5+VEj9MPKEHxELzUff/6Y3v5E4jrNT27TGFYimqaiKioKCrukkQuBFHi+ef47P/tlnKBllJqYnOH/xHFPFArt3zHBpdZ3nL63x5KlL/P3jzxIbJRJs0iAi6XtMV0u4TsJv/sZ7ubr5EldWrzKIE05+7xSXmz1e/z/8NIeP3IWTL9MP+qiqhmVZCJEMtxzhGHecBZ1mloMkSbBtiziOUVVlzNdKkgRN05AyASR9f8DMdI3vfe9JXnbrbZw5/2KGI44EK8ttds7OYFoqg6jDwB+gmxbFUhkRBWiqgRx4VPMGd928yItnL2JoCkGYpQIjIoJuG4eI3TMlJlyVt77+QaLNNR6692ZyMiIMBqSqSt/36XS7VKemeOKJ42DobLb7OO4Eqarj+32kVDAMCztnoqQKrmXQ9zoEgccn//pb9MIBgzCi3d2kXJ5CJinnTp/lyOHDGIpCo7HJwUM30u516XdaBGGXHbPb8X0P07G4sunz6S9+l/UexCKmPFHJwjGiAbZu0t3a4q6jN7Ny6Sy9QYjj5omSGKfgEschkYwxTYvZ2Tm2z84NHzADy8oyAweDbPuXrdOT8Zvfsi0Mw0DKBGtoFq7VpoCUyy9dYHP9Cs89/yKtXsyFK2tITQfHyj5n06YvU5rdAZZT4vLVdTrxAFW3Of7kSRJpsuH1eeX996GrKeVymYFIkEaOrz56itZAsCEUhKYgZIhMExTVREkSFupFDixMkoR9+p0ufd8niEL6/RghElZW1vD9AevNDR568BVEieTKcgfVLdDvhwgZg2YR9bpU8nm6vZBQTdl78AB7Fveia+awvQuvGZABIeKhrSbbxl27X0HRMn+tAmiqSizi4ecL/X5/7AccUULtnAUoGTAgSTJDMsr4Ja9pGpDyl3/1Rd75znf9QBaaH6iiUhSlDHwCOEQ2qv854EXg/wLmgUvAm9M03VKyY/L3gdcCPvCONE1P/Pe+xmDgj1em1896oijKRGdRNMweU9F0yfraCoWcSdlxWV5aoeBWMIsVLje7fO+FM6i6AymIxCfRQEQeeSS798zT7TUpFFS+8dVHqO+a5chtdzIzt8j+/YfYdeAwnU43w4DYJoYpCIPo+6KrHccZR2GPQhqjKGJionwdX1ofl76jMjpjwuuUci7WlER3HS5dPs+g06XouCiYOHWbF5aavPvn38jWxkW6nRbrWx66YVItF+l6ASCZrVXYWlvhwaMLbJtb5PLKCoMoYHFhD153i7W1BuVyGVXXufTCs8zV6lx+8QKlUpmeHzE5USYIIraVyuzfv4+jd9zJt574Dq9+zU/w6U9/nhPPnKSQ1zOqYyJpd1qUnCLFchlTk7z9Z9/Bl559P5OVGlutNqoBPa9Na0tg6SYf+9w3qDom+ZzJzR0TjRoz8y4XXzpDiKBcq1Lato+/+uTn0At5ZAq27mTyA8dE0zJbk0hsvvbtExzas496EtHYaiESSaJIFhYXQNXZvm0WkmvtyuhechyHXM75vmio0QhBSDH+mYZhgFsqEvg6OddhenaGv/nbv8YPVTphF8XUkfiomqBQsJmtVZBJQOB7OIqDjmQil2mezGKRHjpS0/nU57+EoUNCNr5A0VHyLpGW3TMaGcLZzGcMdFfT2bO9ghG3MW2XQFWp1+u0Om10Q0XXVXbu3EOv12VhcR+f//znmV3Yw8rKOnHJJUmy+e5IBqMoEsPM0C+ZjgkSkd3P+bwzvlfj4X06CksZgQhH6TGanrV7pm4ikgjTytDPQRCg6SP0cECpVByjjUchN9nMkHHrPZYcqT+c4OAHbf1+H/hqmqZvVBTFBBzg18hy/X5bUZT3keX6/Tu+P9fvDrJcvzv+e18g49kEY3GeAph6JugUCkNUsSRToakc2H+IYrFIv99CBaQIWGk0aagqiulimyYykdxy21EunzyOkFAqmxQrEfXaLErOwZmeYWrxdu7dtw/HySw3nU4XRVGJ4kxnE0udQrFCMPBRUcec6FEo40hUOPr3I63IiN8zGhyOtiFJDHESoRg6iwdv4YXHv8PRw4c4u7TCllQRfZ9A1fnND/wBP3bfndxxzz7kuTOEfQ+/2ybA4Ybde2ivN7BzehYA0VkmJ9rUJ8r0N87TbrbJWRaJ9Cm5VVzHpN3vMjFdIxpETJWniCOJZYbcfseNrDTabLT6PHN2mS888utsbHiUcirF4gymHeF3u8g+WJM2jStL7NxR44Mf+T1M16bVaZHEAk3Ryds2xakyaxvrbPgB3UQitjzOdk5nHKSve1QqRfr9FaamaixvnkcIA9PUGQR+Jn40TXwvyOaW6MQqbPgRj19YplooMjNVYdKA6dkZ7HyFUrlKmECYCEzLRtVUnCEkTsTXoG8jrtIo5HPEMPPjTNwoh3+mWjbPnTpDP8wOaFVKbMtm5/Yq0UCwd88eNlfOc9sNdQb9iAnHRVMimu0AxbY5fuosWt5F6DqB6uLoNkJ0ydsZVTabzaiksUA3HfKqiRGrRLqOrbWpOLPUnAoXLl1kYqJMp9WivdliamYWz/NobrVod9tM9Zpsm53npptu55aj53lhPSBUHQZ+QL3gMlOpsn22xtbFS6SpgFRFVUxkGpE3TJRY4ugmQRCg57Lk8VzeGVdGqZQYVubJE1GElXMyxpuWFQoiFui6ymAQkXeKWcsXRZRK5fFSTFVUZCoRcYChqWgqRMMtbtZm/hMO0xVFKQHHgHdkB0oaAZGiKP9kuX6jYfrIiCyEIGfZDAYBpq6TDLd91yQKMLt9gV175rl0+jyWnamPNXXYJ9s2lgmlosPFM8eZyJuoBZv6tjL1bVUmKjXufdVree7sEvfe/wBbW218PxhHW18Pug9EZr3I2TbxdSvV0Q90NFdLEkkUBeOFwOh7BYZcnmH8dTpkVysqt9xylFMnHsdMVHbPz/DM82eJZFZFmsDZlRUaX7nErh0zzGyrU55waXqCCxcuUi2XqdbmWWsu49oOk4ZJr+dhWA5zO2tZSoqq0O22qdXqlCZMWv0WpXyRNJbcf+wY5YkKv/+f/5ATz5xieSNgZmeF6uQMu3dXOff898hrJmutFrv2LLCcazM94bI1aBOHPkXXwXV1Bv0ADEBmBIuVlRXcYhGGKblpGoyhh3nXxQslcapzpdEkTrWx0LdYLF6XoBthGAapANXQKVgmfhDQ7QcYSsSBG/cRhyqKLWl7HludLnfcfdfwheB8n/kVsm3f9ZaPkdCx02ln87wwGrbpmUar2WyiazamrZKzMj+comRaouXLl9AV2NjqYms2jWYLx8qEkqSCV7/8Lv7h249jqjamZZKqIBUHPwxAU5HDWY7jOIRBdn+ravai3T5bp1QqotlZupFhmUgPcvksMblQKqKZOvV6nWdOPIudL/LoP2bxW/2tNrGWVUHrSQPXLPPC2fMkmORtm2q1moUs2DbRIBjPfC3HIVWyyjLbmqrj8JSR+TjbvmeymyzuyscybXRdxXEylb9l2eNczeuXYtcvN6QcBgOb11KXf9DrB6m/dgEbwJ8oivKMoiifGIY8/LC5fv+vl2WZ483CqOcdbQY0LUvZGFkDtNQkjXVe/ZqHufu+o7hFk1Ixs5mU8yYFE1xTEnvr1CdNqlWXhd0zLO7bR5zqpPkiXgx33vsArc02cSRwci4izh6Ya1WTHCNYs3aCsU5q1JaONpJBEGBZmep5RHLIDigxrhJHMfVxJNA1kx2zC9x+7zGcShlLkxyan2Wq4jJRraCXXM5vtHjV697CzK4DfOvbT9HxBLXpKusbTex8ma1+gJFziVMV0ylSrtZI0FEMGzSbdnfA/K7dbJ+dY3OrTX12lpuOHuXlr3mYT33uC3zo9/+cF5e6xGqFm248ShLbXL60wqlTpygWCwh/QCGX48rqVS43V1ldvcLLH7ibI0cO0mpu0dvawtI1NE1lqlbDLrjkCy5e4BMkWSxSPp8xzfN5h14o6McJXpQgVGN8mEdRRLfbpd/32NpqMRgM8DxvrHhut9uZti6V2I5LoVjFLddobXVptVtsm62j6CP93bWsucw+Eo3vpVGiCmSbv3zeHacbxXHmVdvYaALgGCYFx0FDMPCaGfJYyQiwuuMSpCqNtsd6x2d6bpFOv43fb3Pl/Glee9+d3Ht4HzMVE8cAjWE3oKoMwgBU6Ec+UgWpSPqRj2VJyjmdaqXIs6dOEcWC1UaDREoKxSKdTpeVlRVardawcs8e3WPH7uGGxQWMFHbU5qi4RdxKkd0H92HnHOIgO6gvX76UdQpRRlZNUkmSZhu9NGV80IwAlJmcQB3HXY10gIaRRWKpqo6TK5Kz3XE7N1KpA0NLjToOCR51FBk+PNtA/pMy04d/5xbg3WmaPqEoyu9zLb4dyDjpw3DSH/j6/gDSOqPww+sPCUVVIeW6Qd1wC5FEgMrddz3Ayaef4u57b+eF753GKukUckU6vS5u3sax6xluZaqCYplUts2z82CNO15+P5vtLmGalaGWpWfo4RQajQb5vDOukpSR/sYyiaNMyTsYBGPz6UhD4jjOcAUsx4fqSNSWppIgyH7QKtmmJREqiVB5w4+9hf/j2ZNYOQcTnXK5zNPPXyIUkkAI3vcbH8E14M/+6COcf/4EX/zyF7jnvvuzLYsikUn2tnr6ieMcO3YMr9WmXnB44cJF9t54gLWNJrl8kdr0NhTF5S+/8HVqtTmubkY8/fyz3LCwh01/BVr68P9X0Ot32bf7EMFmm2So8s7nHCJ0vvqNbzBdqyK1Mo7ZpbPVRrVMrq6vktMznYxUISFFkZIoCkmShDAMsU0jq5qGxIuRrcOyzOHBEiFEBkm83vKRzzsYlo1MAyLhs95cYRBLvC2PbbN1NpaWOfT2PaSKPh6epymYwxfFSIPnuu54PjKqnK+PHPeHM5rBIMDJYg8J+xLLyB46TQjmts/y7ZOn0XMmRDpFx+bbx0/wqgfuYmO9werVFc6fOYuu2VQcG00JSAKfSM8qpEgINCNrwdThal/RYKLosKNeJQgCilNlqoUKzWYTKSWlSpkolDjSYeGGPVy5usyePYuceuE0X/ziF5iq1wgiwcXLSzgOdALJuQvL7J2pUixluYILCwtomoquZHYrFIkGGUxS1+l2u9+XRzAKahjx1TMtoEmaZpt3Xc+qItctEkX+UEOVVaaj3L/RizsO5fBlIMbP+SiQ9Ae9fpAjbRlYTtP0ieHvP092cP1/yvW7PoB0YqI8NhiniURJsxsJVSfV9LEhORwEOLaNZuhZ2Gdi8ov/6n3c98rXs/umw5R21QnLsOfgAjtumOeGmw4zOTdHYLjsvukejtz3ava/7C68QBDFEsOwCVMBmkqqXHsLjIyspmmSt+zxelUdDg5zw54+TTN/GVzjtwsR4XneEEimDtnv0TVmtKZndok0gtjH83xe8eCPk6/OMDB1vE6X23bPo3geiWUiXQdFt3nbO36J//RHf85vf/gPaW22ubR0kan/h7p3D5Ljuu97P336dE9Pz2NnZweDxQJYLh4EQZCCKBqixIdoiqJlWZRVjiw7jK6t2IpjO7Gdq8p1xanUvYlu4rqVcnKTe12O4+unpDi2rhzZsqyoZFmm9aBkiqJIECJBEATxWICL3cXu7Dx6enr6cfr+cfr0LJxKQlVyq5ipYhXIJXZ3erp/5/f7/r6PpS6XLl3Eb9Z56J2PcPbCeR5973tZ3dzkTd91issvX2YyVdTcJr3tkKdfvsrLNwZ8/DOf5b0/+MNMghDbErz3+95Lo+pTX6jT3dfmU5/8PaJxwCTTkfT5GJpVn+pChyz3mZtb5NzaGmEq6e4/TKPRYaG9yMGDy5w4cYKG59NttliYr+PXPLy6i+UoYoVO78kl06nCrfj6mmQx8bhPlE1RRHg22FNJteriOj65JQjjAFdIbr/9BKsXLmKNIxa7XSbjAK8imWv43H74MCJVeI6H79XJFGAJXM8tTN1SlII4Ufie9mpK0xjHFWToIE1PSg4cWOTInSewHIlX87BEShwGNKo+w34fIQUOgooH2CnC8/jdz32FJ86eQ9QkniuIVMo4COj4Hi0R0rBSJkEElksSxeRK39+TOMbxJOsXt9i3t8OVV85RVXB9bZOK55MiaCx0iFVArEIGwRa9wTqXLl1kodsBBffedQ92DomMyCwtcA6GQwbBFqkIWdi7yOKeRUgVaa60iZ1SkINX0Wxxk1RueFEmSdkc2I7jlulOjvRwpFvqEw0wbmK1hF3EY6VaOpbmilzosVc4EkuA40os8drpCdZrAbQsy/oq8BN5nr9kWdZHgFrxpe1dYHo7z/N/YFnWo8DPord+bwF+Oc/ze/5L3/+OEyfyj370N8vKLeXMNG8yCXHs2bYGtNm/of+byl+ruyRpSK+3RTrWtIaFhQ4LC22iSUqUKkaTGOl4CEufGFqgrEgi3THZlsAvTl2gBF3/aoy8kQHsTugw4KwQs42JIdEZlr1pqZvNZkFCTJlEMbW6yxf/7HN8/euPk97oYU8UP/jXP8D/9Vu/SWvPEltX19jZ7CEcSZ6GzHnwob/9Y7xyZZUDnTZLB5ZIlOKhtz/EV77yBPV6k5fPX2Rra4vBJOI//uEXOHhoicjVGMJwEFKvN1lYajPuDxhs9xAK1tbXecupO7l+dZU9nTbD4RCBpFVvcuH6GtJ22d+Q3PPmu/njJ55ip683sZNJRLvdZjDol7KVSsUtuuEUy3GZZopqRWJbgnQaM40iHBdULlBCYNkVlIKKA/E0QuLqE9prkhHTmq+zt9mi3fa5duUa1YqPk7hESUSqYk7dc4pXh31+8qd+mrfe/wCTKC7GmpQ0S7EKJ0shJBaCNJsxrbNUZ9YlccSXH3+czY11Ll+5wPbmOtNRwGB7C1tAvejENsK4JB1LKRmNhgRKUCHk1v1t8iBiHEOzUae70GZ7a5NX+or+FGIFdp6SKIXnSuIopFHzOdr1+akPvocXnvk68RSSaczm1iYL3S6jccCJoyfZGfapN5o4FZeF+TrPnj3L2+5/kGeffoY/+eoZslqHNImpznWwp32Or3SJFSwsL/P2d7yLI0dPaB2kNXM/0FvptLxfd3c65r/Zu0Iadlu7GDcFU+DMPe1VNTZo6DtKqXLLaGQ5juPy2GM/ygsvvPiaqtVr3fr9HPDvi43fReDH0d3YJy3L+lvAFeCHi//3c0WRuoCmJ/z4f+2bq5JkZnxsKGbeQrGt9Bsz2JUJJdWOkopqtY7KII1T2q1lJlVtW5zakle3QmwFtuviVeqkqSLNY0ajIc1mk4p0qVc1sGkLeVMckBYWa+wiCAKqVW+Xh7TE80TpdGjbeuywC9uYWVw2hW5R4wMGvE2SuNgcSqI45W3veITtoM/6+XNMt/v84R9+gkfechdfe+YsfsNjft+dXF/fYrwzZGca8pF//uucess9/P7vf549e1ooIfjEHz3OJAgZDgNA30jbYcSDj7yLP//Kl7jrTYfZ3u6xt9NkNBpy9oUzdBc62CJhMg74hz/zt3j8y1+is7DAjZ0enufSnmuRx4paxcVzJI2Gz8ZOD1tI5uc0Hre5uYnvScZjSaPRZDDoM53GzM21SSd9SANyBAs1n4ot2AlD3nBsmWm0xVYQM4xzlARrKkljhXR8lKWQtktuCzpzbeJgiN2MWb28DrkkUzCYBgxGQyqexzdeOIdbEfzO73yU+lyLW1YOo1Cl2D2cRnhCYFsQDAMqVZfRaMihAyvMtToQR0TjgGsHL3Dm6WcIt3tMBwFbm5v6EHJdprnCVjEVAYnS42KKYJIK4jRmoe6x1OmyOV1DFLKqPFfcd999PPfJz2O5OsHb9z1cpSBLqXs+cRhxdOUYzz37DIdXjvKlrz5Jt1mn0+mwcmiF8xcucHn1LHv3LbG9c41pEvPyhZRxAlevr3Pu/GW2R0Ok0tzD69tDfCJOHFpiEkbsBEMO3rLMdBqV6chKxWWxMviSoWuY5cduupBZGjmOLDEn48yQ7Nqu+r5PpuLSYsbkABpZziz5XKHUa0eLXlNH9f/36/jx2/KPf+y3ygtjxiqdPZYiBeUbtAuago7diUrhrwFRASpV76bqbeKZ9DgnS0A1LtTn/e0eNd+n4rhkxRp79+YPKIsOUPi1i3KW3z3XmzjrSsUrOytzshhumHFWkFISqxQ710z8Zt3ndz7+24T9HuOdLYKdLbrzXT7xH7+A40oW9yzRmOsS9DaJk5QbO0MefOABnnr6abBgfr6NymLiOGVPZ5GNjXWWlg/zrW98nYcefpCtzT6jUR/pwJ49HToLLQY7W/z9//nv8mef/xwvPHeGvfsP8OLFi+xdWebSS+dIgohOo00uBPO+i1+XnLt0kcsbEdVqiyAImJtrMik2ScZ3aG6uxXYQcHge3v6mFe4+dQ9PnT5NVUimoxAXQS5iJjHcGIW88NI1phlMMpdEuFgVLRFKKz7dhk/DEvheShCFxAkgPAbjHtVak+EoxG80cSxtOX3biTv51X/7a6QqBRS2FExUjIOGDG5dPswz33yaj/7Ob3PpwllsFbN9Y5Orr66xZ98SvZ0he+ddhJCMgog0VQjLpeLCwf2LhIOAG+OIfhCS5gK/2WIcBRz0JQ++6ThPPPkk2D7NRh1pgbThhfWQ3kTpxYeAXApUGjPXqBNPQ/75P/ow6xeeIh5usr3VZxql+HWfcRThVFyqjuD4HSd4dW2N5ZUVhHQ5cvwkWSr517/0rzizPsRx64yjgMQWtBx4y8ljKMuje8cxHv3e9+K5dRxLd7AG2DaBoMPhsNwE6lHQK/E8lapSXWGeAXMYGxmNkdTkuR79jBbQjIVma2h4WnEc86M/+rdec0f1umCm/+q/+Tcfed/7fgCAKJoUol7Tmgri6RQhJRkKXbU0C9yIHcMopNFskqkU6ThkWVacAFbZxupR0sJ1nXLt6jgOyTTCkZJ6vUaazcZLw4w3UUKVwkETzOo2LRjn2hhfs3C1AV2tVtMxQQCFLQnkxfpWG+rr398hm06pFIQ8LIu3vunNvLx5jY3RNjYWly9e48QtB1nev8jpF88hbMWexf0Mg4jbjp/g608+yYk3nGR98wauW6Ozr43XbPDCSy/R7Mwz6W/yzu97hEurl9i4fp716ze478238de+/x2sb1wjGg/46le+RBJPcZs+kbK5/+3v5qd/5sM8+Rd/SRQnKJUw5wmuXLnI97/7+4ljh/X+SF9jG8JJiFtxGCdj3IqL69ikccIgHPORD/8Q4+trZCJiur2JikNubLxKc65GTMD+TpuV7gJ75hy+9977uX7lZe59y11sra8zyRWi5jMdbrO34RGMApwsZ093AeyMthTcun8fo1ev404nTKs2biro9TaYZgF33PEGxtmUXAmq1gLteot4OODHf+T9/P6//ygba6sE/R1GgxGn7n0zywf3MtjeRMgK5BaHD68wCYZUfQ/hVohGY4SyCCZjbgx7+PMLZLlLnsS88fZFjh3ax9mzF5gkNVInx5IpR44coNvpcP3GiJ1pQqVaRaaQ2hlCCsZRhOvX+MQn/5if/akP8hdf/BOWD93CUucA+7r7cAS0alVajRYXXnyFncmUVzY2OffSaXaub/HpP/4Cz710iZ04I0kVe7sHqc03cPKc247cyjiOqdR97rv3beS5hRIZVU8f5EkSMxgMCnWFIMuyIizCLEF00EPFq1BvaLnVNJ5SK9KpNTQz0WM+s6AI3WDEgKXZ7wosRLkhF0X23x/8wR/yMz/zM6+Jmf666KhOnLg9//e/+zG9Oq24N829WabIsxQFILSXczLRm6Ak0RQACuynUtHKeEMVsG3t82zwISMQrlQ8RqMhtVqdvoRPrgAAIABJREFU6TQqqQO6tdVFQxuwifIDMR+mCUU1HZFh3urio/CqXmkul6YpNd8rf0/zvkqxJpAVI2Aca2V/xZYM0oCXXzrLU196nCxNUeOQLE0ZTwKefXGVNIFmo0Wz2aLVavKtbz3DysoKaZoyHvc4evQwO/0tqlWPf/aPPsIv/ct/yS1HD7O31cQScPpbT5NEEWEcM7/Qxq165LbAb9b5rlP38cZT99EbDvnwhz7EY+9/H8/85dfJpyH97XX2HzjMV75xmqmrr0+9Xi+vxWAc4Fc9/IpHGIT4FcHv/t8f5huf/xK3nDjK9aurPPvM6eK6SlIrxlWCeBKDlEyCHq3FJSZIvn3uAhevx2SNDrYS1IRgcc6jt7HJ/J42QRJiRRGdThcQ9Lb7TKR2jkztlJ2wz9PPnWVzq0fFknQ7y/wfv/gRvvinnyXPQvI0pep5VKQkDEJsF/bv6RBPI8YRDDNNdF2cqxMHQ+YWFwkHIXWvyY1gSCRhPE2ZjCPuecNx5uyULArZ3OgRWz4qDdi7t4VXkdi5yxe/dY6x8LCkS1UIUjHDLKWUiEnAv/hf/x4vPf049ZrHq9fWyNEE5zSLecPd97B1fZNGu0ml4ROOFG+97xF+4ud+HuV4DGPtqFlxPXJP0BRw4sgyqZTc+dZTvOfR9xFPdXfp2LLEdy1LW7LMzTXJMsWNG5vlFtvzvJLAbF6WpW2TDa1DP5aivP91fN2sObAsQa5mGK4pVFmW8oG/8UFeeOHsa+qoXh+F6vbj+cc+9ts3gc7mwVZK2xCbSHWFQsXpTYGj00ITaDZ1YRiWZLPdWzwD/pmRzjCVkySlXijMOx3tIgmzmOrd87rhihh7X6MKL1etRSttfo4tKMFDzTER5fsyARZm5g/DEMeGzBbkKiWfRjz+1cdxheDC82fozLW5vNnj3IsXaDbaDPpD3nzP3Vy+dJFLF89z8o47eez97+djH/11jh1ZZhz2sTKfza1NlpaXCUbDkheWphrQbXe7eM0mtfkOj/3IY6hMMp7ESFfy4b/7E+xcW6db8Tm4f5HBzjpurcULF68RxOitTvFehRBMM+19buV64/X9951ksRly710P8Cdf+AztuSatVptLly7r+PYKEKU4QuBUPaIgRLgeUQJ7uouESvDZx5+EWpfYcjnY9KhZiv7OFnv3ddnZWidKUxrNFsoCK5NQ8Vnvr5MLxb/8tV/n8PIJ9s23+J9+9APc2FonmcaoNMW2FBawuKfLjY1NFCkVS3Jo/wF6/T7Prq0y3+pw55EVju5t8aVvPIGrXGr1NmEKr1xfZf/BZUa9TZZaLrfMLdIbBigh6PX7zPkS33NZvOUYf/bEU/QTHSwxiWLqVZfxJCw3wpYFHops1OfDf/MHiMd9lBWR5SGdhS53330P13f6XLhwnrNnnuf4saM89fQaX/72ReqdNpkUzDe0yV4URfjNOk0Hbju0jDPX5C1vf4g7TtxNrgppS67tiatVbXGTpqqki5hU4zynBMArFa/Y6BXPHYZbqBdL9Xq9XDTlOdiSm5ZIXmX2PfNc28q4rssP/dBjvPD8aytUr4vR71f+za985Ad/8K+hVFYUD7tYl2rRpCMl4WSCLW1tXqW08loUQYayAL7zPC8N64wgWOeSZSWlwPerJQs+jqcF6TBnPA4KG4qsFGYqlSGEFlYKoQMaZ5SDaSl8/aviY/3ftFBZZVnpvZNlWbElC3EcB8uySAuiYZ7nOI4DAmxh4VdrZMrmjXfexThJWNvcwql43H33XWxubpDngs6eDidOHufv/d2fJBxt0ag6vHLxPMl0SsWycS2Xtc1NbFfiuA5erYJfqxGT47cWuOWOW5nf0+Ud3/sob3/oexiHY8h1Lp7nOoTZiNVz51msz7F1Y5t0GnL8rjfx4qVV5ub2gKXKznbPng7jMKGzsEAw2iFNM5bqDgcOtNgZDBlEW+xt72Vubp5Bf4hSFq7nMxmNsci50dsiyl0mk4QsywmCEdu9q5w4vExvMKA3GrAxGJDFMRVXMI1DPAG+X6M/GhBGIUrapJaFtOD2I4e4fHWVD/7o3+bRd7+LrY0LZJlR7+elp5KwoOK6WDWPLIbxaEJjzuPeu+7k2+deZnWrz9yeNnGeMddoEvR6pFHID7zzYa6+8grf+/CDOKTc2NlhczBkEic0PZvmnMuBpQN87fSLrMWQWRaTMKHVajJNxjjCIUtTpG1jAVZFkksHFUWoJCYYj7n/bW/h2WdfYG1th6urV8CpMtfez4tnr3L2wiqyvpcwi5E2TMYxQTCg0ahScSrs37NAteKQOTb3PfBQQcykIE3HBaieIaVTQBp58ecMKW08TwuLDXlZShulciqVCrnKSJIEz6vMNqdl5BbF85gihF1gtmmxFcywLIusEDt/6j+89tHvdWHzYjofkCUIvRsgTyxdWFR6s2zFmNU5jluQ58CR2mbV8EKwBHEel+D6aBQU1qnpbFzxfVzfJ1EKVZwq5elg6dETSzKNI1xP68hqtXppnVypeAV7N8JFby2TVDOfjXC5BN4zpe1xKbx+ilHQLeKIrML4TeUx0nXpDfvcdutx3nDHSc6ePcuffuFzdG45wM6gj5CSr33j62xsrPGeR9/HS8+f5cq1ixxrt8mikHrFI67q7LlaQzPXp2lMo+ZhVVze+fD76HQ6+jrGMa6ok6QpQgrCKGYcKRb2LrK1tkaz20LGkq8+9QSjcZ9RqBA1CPoBC80Oq1fWabRabG1sYgF136NmK04cu4snn3icA3NL2LbLlaurnPyuu3jym08R3IjoLLTJ0xhfwVzLJ00EwSgCJUgzD991OdSqE2z02ETyqpD0UNREzBtuPY6VpQRhSNvzmCQxaZgSJik7w5AXnniKd3z3PVy+fJlaQ9Cud5iMQ1Jb264k04hgHOO5HsFEMd9s4uYKmQv2zTdpVSS51+Ivn7rAcLrO244fZaHpMo7hyS8/SRJHPPfNM2SjEOVL/IrAE4qaW+eu245zdj3gylaoLYZtEFInLE3GUPFdxuOg3HLnE32P/+WFdTzPY2lO0fzmecjg6qXz/LN//dv84i//Ot88fZbN9S2cSp1qxcN3PJwKbPViqpYiGEekMmIQSnzXpb7gU/ebBKMhlYqL60hyXKKppmdE07iEIyxLj2e5LbCERFgQRzG2LLzWigAI0Jq9JJtpWIHZYinXmZHRNKZWq6NEjONKvf4GnEK0/B24vLw+Rr/bjx/PP/7x377pFzer092z8MwmQj/MnueVjoEavJtt6szfdV2X3KLc5AGEoVZ6T6cxVoFNaUZyqI3K8hkHShZYk2HoCjlbt5qwSyOTMSOheRmMK4qiclRNihsDKCQ4ohTK+r7PtNiO6MI9c080879b0cziLz/xFf7iy19iz0KHZBKRjCOkJWjM+3z/972Ll759hv2Li1DzsNAFensQcOjQCve/7UF2Bn2SWI/YjqPFqShdYLNcYbsuT595kn/3K79K1/UYTfp891vv4avfOs35y+tUvSbhNGA61cnLo1FArVlnOg6LCCzFyT2Se99ykt76ZVDQXVzixvYWfr3Ojd4W437KQrvFfMtjGoXYUrK50SPPJVkKouajooD23i47g5AnXrpGL3aRAhquYBpG7F3QRfnIocMcPrzCmRfPMo4i+sMAy9MHjlKKv/N3PsRXH/8SnVaLq9fXGI4DyBQV12XUHyJrPo2qRz6NaVU86nXBtZ2Qqzsx9VabOAtZnvPo+i7r25vMd9r0Bn3Go4Cm5+NUJHNzPtub6xxZOQEKPvVnT9JYWqY3GDLf0mEWw36fRq1eSkvMZtpsh0330Zlvk/a3OLS0xLW1TbbDPrnrMTfXor/Tw/E80kRnDTiuwKsIWnVNEB1Mezx870PYucCZb/ITP/Wz5XOSZSlVX7tvDgZ9HTFWSI+0Ls/DciSWUkhbEgYB0p1tz8u08uJ3NdFaBgpJklj7rJdBDjPOVvkqyNDvf/8HeP41jn6vi47KMFTNyrPEPKYprVaL4XBYiFVvpgX0+/1y82fcC0zBMYxaoLwhDO5lOFKWpf11tOcV1ApRpf6aAIoTwJFUq/qmdwrC20xCExYfkii7QdMKG8BcSk0YNbH0poCaAmVuELPCBWPfaqxu0kLeIEliRbXmct+9D3LvfQ8y12oy2OkzHg5ZXV2lt7PJK2vrdI+cIMkU+1YO0Fno4Hk+w0GEIwSDfoSVSBxH/9zxOCDLFG5BtHVdj3EU4gjJHW+8ixsXL7O3uUgUx7x0/iK59MniiMMLLX7+F/4BL1++yKWrq7zw/GniKVy5ssl8q0tveI37H3yItctnef6FCzzyyCN89WtPMBoHzM21sG1AKja3tqg3fMZBxP7lFQb9IRsbm6hBRJJGiJ6i0fDwSGn5dZKCL5RXmlzvh8w167ywusa3zl3QhFsLslxRlS7YElvCJz7xaRYXmly+dJ40A0e4pCqls7BIrVInzSKyNCVLIiqtJtIFr17HTyKub6xT9X3W85T+IODWY4dx04gsdNm3uARARSkaFZ877nmA0Sjl8WfP4ix02AkjvJqPFJIgGNJutYnGOnLMK8amVqPJJI5L2+08V/STkCRP6b+6TpoJZKNDvVIHK2W+08au++xsbOJJgZWlvPHEMiqKiVLJGw8eZ9/eLtuDIYeOHEZKSRD0C8lLnXCicSfP8xgM+ljA3FyLWk13+sMwxHNc4mlYAumG4DxTYKQl3cZ0SvrQFqXHutHDCrTpniHJTqIApWa2PK/l9booVKYwGT6S2YaYh94t1NaGrGa+bjoqA+QZqwljQl+peMVoOONOmZOhNLQvZmgMo7zglkgpSjW5UmrW8sZx2f1MJmFp6WKWAMYhEW7mXg2HQ726tQ2edjPHyrw3wyMznYC03fLGEIVOK4lBSp9UpUSDENeS1LqL1OtNHShpaUsPyxZkxCSxQNqSquMiiwLuVj3iNC7N/KVUoGbdapqmLO5ZorXQYdLvkw6GdJcWka5HnrvE0ZBj+w/wJ7/7q9iWQmUx9y75rPUirsQ9Ll0PaNoe//HzX2TaX+WN3/UAv/Ebv87RY8fY2NgkzRUV3yPLAOkTxxK34jEaB9xyeBmv5jLe6dOf1CGDqJdy1+EVvnR2jUjEOBLUVOD7HtE0ZpKEVGptwmkISt8zwTgqo55iqSO05moeKhPU/CbTSYQ1HuJlijBLqVc9RnFITkqt0eKV508jGl3m222EB2sbQxwk6sqQ9VcvcMeRY1y5ssnD73yEyeYWq711/ujrX8Spd4nGERE6Q9HKYgY7PTrtDsOdPu4uS22BYNgfgjPrVCaTCD8X2n3BcZmkio7v0fBavLx6gdp8nXDtIidvPcCpEyd57/e+hyvXLvKVP/8cueMSDgKuZtdodrvsP3iAaTHmbW1tlZo7pcIi60/q7WfFLblQtZpPMBgy32qRxilxGjMehyWEMXMOKbbtxgm06KKMr1UpzSki35M0ZjwakubmGXztNeJ1EZdl2kmz5t6dG2aEvWY+znNFrIqYH1viFGNUrTAfy3PwpH4ok0lEq15HCkmWpIVvktYNSqHXrJqJrgovLEhyXbSMgHg0DkEIcktHCFm5oFatkyYRFVegUm3/kUzjQkumCsGrviEcW8tGPNfDymeRYLYtioKgyk4Mip9bzO+WBWkWk6NJdFXfBSvVD2kS4tkghcS2JNNxhO/62NJF2BLpCmwbfNfDdz2yaURmKSZJRG4pJhNNzEwKv+44jsEG19OFaq5eZ3F/lzSJkDbUay7BNCJRehR+7L3vZKpSgnHM2RfOk6uUp544R7PWRiVAkqKclK8+fZbmwgpnXnyeRrdFjOTn/v4/5Od/4R9w5PgB5ufr7D/QJbIUSgWMJhEvv3KZt37XW6nVfNo1sAnxapLE1iEHnuXpjtB3GUUhYRJT8eok0xCVpkjpEscKKy3oLbZkO+wzv7DI8cN30q7pUTZ3FNd31pnaMY6lsPKYQ4dW8DyXW4/cSY5PnEtu7PQZDPV2UXou69tDEppc2Ai4HsG/+/QX+MTXnuLZa0NC2SLMILQV3cU20+lQH7S2YDDqYwuFIxTSFlQ9F68ikTbYDrieS6YgSRVROCTJYTwOqEjBdm+Trf4mnfkmNQn3njrJ7fuXWTt3ll/+F/+Uj370oxw8epLjt5+iVvF0VHu9zrGjJ5hOtfHjwkKnFM0bRwTP826SxSilyKYxVc9jMgkJi4nBUHsMwdO2ZWkaqTfJGmPVBG39XJjnLrcESZYW9CLIUjS36jsAqV4XHZXeiKWFc8LM3sUkhhiMyLxUmiJcD1A6x60YtcyFVrlu/Z2KXgPnhTfSbv6TwQVc1y3lMVEUkRRaKIMjNZt67atnbA0CJkmsOwFmJmCgpTSWLUjTuDxdptPd+qbZzzX41O7uTCv+Z8D7THpA6e9jVP92wYUBXeg9X6f4uEXRM12diYlyXZdpFpc4g+1oyocZa+M4ZhrHxPHsOlc8l4MHl4n6faQj2brRZ26uyeb6JpNJSDDuEY0V0q4TjSV3vOkU271Nfu4nfpgsl/zG732WK6MhteWjuGtrdBZAZfDL/+oXaTbr2FUXFaUstLuMRn3cap3OQodoEvCtZ77OZBrRbLXodLukacrVS6v4NUk/irFdTTQ198p4HBZyDVFggjqxRijIohjh+Tz/8kVeOX+BE8cO05yDgwcPkKmIQ4dWePm5Zzh6/CS97SF1r8mvfeoLJE6dPIVOu8M4jhiPA1Aw16hj5YpgrGks1arPNNaBqHmeUm/4qIFkNBziShfXlrhC4FddVKQPBstGG9EVEIO0JdNpSBJDa65OlmjX0Wkc8z3veief/PgnqLYFNzau8UPvfxd+HnPnkRM8FSs2b/So5CHPPXcGpM5uxILFIhU5SWYcp2rVvwlvqlS0waQB1M2kYnBfY72kYYy07Mg8Txa8R688oPU9L8pNoFGDJElc0jDMzzTP12t9vS4KFVBekN0Y1WQSlMm3QlC2ipViPMGCtFR667EuDEP9YRQPIUKQK1Vo62S5Lg3DkGazWVAFZCm6FColz1KNRVkz21SjK5xGIRaisCJWxIVcxoCJ+a4Ck2UpKlNkWXzTmhYoxtaZVGG3TavBtkyHqGkVfvn+p9OI6VQb6atihMwBr+rfNC6bQlkS7woOTRynuHJWEAeD/k2F3BQulQlyJZhf6BD0eqxdW9PgK2v697ElSRoTJZKtXsyVtXO05iTnnz+tcQsV49aa/P6nv8A9Bw5Qrbc5cXyFC6+cpWIL9uxd4sar6wT9HrcfO4xMXc5dvIhXkSw02tx2+3H27OmysbHO8vIyq70h9SBilEdMphFzc23G46A8xMzYajZRURRRcbThoa1ga0cTGx//5lmOHVjia08+wdLSEk996+scvOMw/+9Xz9Oe77K5sYrf7RIPejiZYnuzD572lap6HqNRQJrGM2G6K5lEutvQI3wKOVQrHsOwT0iAJwQkers7HmvXCktQpLW4ZFZKs9amvxNAljIJI2zHZRgEfOpTn2bv0hJ1L+Zt776PxYbk+SdPc+4bZ1COT7XZYm+ryfpgyHyzhVP16CwtcfTYUe2j5egFk4EmRqOQublmSatxPFkUTW6yzjbGg4b/pHHUuIRSTNEzh2K16t0UD2eseoz+1RzM5mdY/6MlJVuWVT5gMNt4mZnYPECll3OSksYxCrAd/VCbU6LVamm8CWMMNrOfAH2Kme9tioJty7JQGmsL07kYxrphuFuWouLpIqWU/t56fNK+R0khmDaFx/CudpuLmT+b4qdvBAr5wcyPyxQP45GkrxWlij3PFYlSZOh/pmlc3kjmZjPx3aZAGuF3nisajXpZhM0JawBQpRQqURw6dJjtXh/LkZx78SLr62vcd/893PmGE7iyiW37NBst8gIXTHLB1eubxLnLB37yMfb5dY4sLvPi5jW+8dxFFg8d46d+7u9x8MgxXnrxAvEkYl93kWDUx/NhT1cD/zc2h1y6cplvPfsMyysrXLx8mQfueZBcSXy/jbK0qNhx3KJ4UkiVZkZ4TkVTAEQO6aiHLRTDSQhCcmWnT+x5rPb7iFaLjc2Y8QSuXd8kJWV7ax3fBpGlzDXbJYGx3+/Tbms2v6y4TNOUSRxrP/WmTxTFCMvHsSXTMELakoZfhzRFKM00n2+3qFSKwxbFeDwkGA7ZurFJlsbE04hms8X3fM+7AEHNr9MfbCKtiAN72nTrLVZuPY5b91FWymDcY319k0azRWZLUgucqsedJ0/e1GEatYTBfM3BF4ZhuZ0Gyvt2t3mlaSDM3zG4rXFc0KNfXAaymKLnFXQec3CYf9eRZf+D5fqpTGMPxisH2y3BOFMIYLbyzIVmFas0BSXKdtIk4tpFW6kKxi15EUmUzQTOxvY4d11yIRiFEa50aVR0V6IKeoKytEeWSlM8KbEKI3vb9gpTthilZNnOTqeaijAOAqpVH6R+X5mlEK7EsWZAvhYvG0sbjWsFoXbFNHhAbgtileJUPRKltOuvMmZzAl/I8iZzXRfcmeg5K3yHSiV78QAbm5yg2D6VXZ0tEcUNrUM1BCsHDrC0r4tre8wvd7CuC1QQ8uTXnmBjR2fvpeMUT7qoRODX6oxHIaoryS+d51hX8PVvP8M4rXO92uN//ye/xF/7nvto1lzmOz5pnGLXodOu0wuHdPe2SGJFMAyRKLyaYHX1GttbAf/qY7/LtOpRcVu0RIvEjXUhEgKv4uto+tEQIaDd6bJzYwvXkpDH2MKl5ngMRgFvvPtuXlpdY67ZZBoFWEpjiVbFp73QYnNrHSsVWuEQh9SVILcj5uYPY+VDEqUPJGGlONJjrtnh6rXzNCKf+VqTpB+BBbW6TxwNSdMh9Yp+1KZRzDSKadS7uHZIEEa84eQpzr18hoWFDldX16hWfUbRkI//3u9xYLGLyGLuOHmY++9Y5oWnnuGFzOXa5jpvuPtOBmFMqlzCYY9UxaQTGE8Ft88v0tsOcKSHiz4ctSBY4NZ97fumtPe5cf8wVBtjamDbkjRTuK4WiJuDzMq1osKWRmxPCV1Ylm4GzEGtw14FFhqKUQC57trFd4BRvS46KiEszU8qeEqmKzEUAsdxqVS8EoPY7fVkMK1qdZZMbAqaOVlnMdW6OzFF0HVdLBTCgkajrs2+krjsfExLq/106sXJ4eI4XgnC+75fdiGGFT+dRmXbnCUpeaYQaEDddIjG/sKcUuZGMaee8eYSuZZO5WmKV3Q+5hTLMi0fmiaxxqgKiZA5EU2cuTlRwXRYJoU6Lbc0ACabzWBcmVI0Ws2S9yRtH7dSx5F1oliPyrbr0pz3ceswsQKCaR9pp3ikbG1tcXDlAGEGVlVQrbW5vBnxS//2swzyFg8/+F7uvvMerl9aYzIYcv+b34qKI6Ig5Ae+/70MppJX+5KPffYpPv/MKk6njVPzyVTENOoVXWGTuTmd/hOGYWk/0uv1qDeayFqdzG0ianUmgPQ8zp4/T5YM6e2sksR90iwkGq9DvMn6hWf47pMHuO8Oj5/7mw/y4+87xQd/4CT7HEElDiCJSWJFs1mn6ntYliIMA+q1NkmsGI2G2DJF2pBOI/Is1ZtlG+yqiz9Xx/E9KvOS4WgLkpBXXjyLUrB65RqNRpOjR45hWZL9y8v0goCNnR5vOrbC82eepzrfpTdVNOfrjEcBe+fbPPTWUzTmWigEe/YucurUKQ4fPlwQp+Py8zabaXMQmXvJ2AybhHLbFiX9xvf98l4zsjQovNyKIrfbssg8dwZuqFQ8qlWvhDPMMyiE+A76qddJR4VlkSod22PeiHnj5sG2bUEYan2SsTmdVfCQIAjK6O6Zzii9qWPYvVlrtVoApElciiwpSGmGPgDgVDwSNbNoiaK03NpF0xBP+OWqVilFGIblhwLQrDfZ3Nyk0aiTFiRRM+KaTipJYqJIn3imM0zTtHAclVgUv1ehTYRZ0o0o8DCVR0jHLSkeBhdwpNzVsquS9e848iaKheGPGazPKVKEp3HMQrfDjc0e83s62JlkFEbc/5b7GD79BGkcM99skaYp3f2w+soFfM9l0O9jCUEiJH/6mY/z4z/9YaSsszFOURXB//mb/4F5kXLvm+/mjttOMBn1uXytT3Vumev9a/z1n/6nJEC93WEqfNIc4kSzp+00xq26WIXSoF6va1EtlJsnIQT98ZBKxWO+3WTUW6fquOzb18H3JJNwSBTB8soyly+v8vYH3kpva4ulTpMkDHCdA6ydvUDFkSRuyCN3n+Dq9T51mTJKI0KlR5m5VpNwHDEeh2RSIAruXTINqDWbJLmk4Te5urnKdOpS933Go4BMxLz9gXv49nPnqM11uby+hu/XGQ0DXnzxnLYvmgYs763zN97/AV782uOM+yHBGI6cOM6dR5d5/oVzrK6ts7bVA+GSFPdDnsPevV0sS5Ir/XnWavVyqTIY9PE9f1dBqpdFTQiBymcmeHEc02j4ZKlCShfQCU0wGxV93y8XU7pxEOUzatta0WEgBp0FGBaOC6/99fooVECSpcgCAEap8sGZESVnb8yIJc0MnKZp2VHpqm9kOZpoGUezEada9YhTVUYXSbtw8hR6xEtyc6LonxvHsdZiWYKq7zFNAt0J5TF79rTZutHHcVwajTqTiR4JDfho25I8S9m3t1tiTLvTUAwFwWBG2u5YluRWpZRO4bFmQmbt9uCW3z9WCllxdYL0JKRZq1Ov17U7567uTMfKG9LsbHkxS2aZ2duUqcKuJI1jjt56jM2NHt945kluObDC2iuXuXjlImvXV6n5LTa2AtZe3eLRh+7k4R/5MX7ntz+KdHzGOzGTpM8nf/OXuf9Yh6+eWSVKFJbjMbUUF8cpa195ms9/7TRNz2MYhUwzULZLPtdCpSFT2yW3FJ6r8GyPaBKC5TGOUkQa4vs+jiPp9/tUfB9jyuY4Ls1Wi97aZUbBJve/+U6avsfBpS4H9rb55tOn2b90gCCKqZFSSUP611c5tPfk1qNRAAAgAElEQVQUr97YZL6tGIwCbjm4TH9nDb/eYqGpeOu9p3j50mVOX4959doa/X6PNFXcsrJIFITEY20z7FddVBaTRBHDfp/FTpfpOMQXEterM5yEPPfkaaa5YGui+U01v04SKw4cWOb5l89hxSE//IGHufDkZ3j03Y/xyd/7BOM4ZO3qefa1O5x56RrUXFAhdx9apiVcNnb63PfIw0ARQDKNaTSa5ea5UnGRiSyngKxQbJjOqlarlx5vQsw4UwZjMl2YuZeNY4njuBq4/yvKjTAMiVO9+DGF0HXdoov774hRWZZ1Gzpo1LwOA/8Y+Dj/HQNIDf3esgAhtLAxSUmmEalKEUqv4ONE85XMKDeZRAhXkjIjSbroYqMyRZYoBFKvTW2XNFZ6Zi42hXYR7pAksVbgu1JzPWw9y1uJ/qCGwyHWXBPXNdIeQW97iOdqb6phv6c/1IKgiX4bpJkCoTszsxWMYy0zSDNtsG8KhZCilCOYDUmqUqYF32U6ibClBtZrVb+wDtXvIZ5ENKo+aaI3Tu4uo37tKzTju+j2XSAsRY7CdVzSBHIrLZcJZrQG2Le4DOJJbjt0lHOXVgmTmK31Hkf2LzMchCgJqZ/yl8+dod/v0Z7zCeIYW8TkqURNwLUUty4d4ProAnNCML+4RNDrkeMxGIcMpzGuq9fiKo3otruMJxBP+/ptKh9FinAlrXm9rVXTlHAck6Ux1apASJeg39PFl5Ttaxf54HseZM6DVzfW2TPXQsUB3/zWBRzfY3Nni/5OwJ49HTbW1jl25Bij/hBHCKKpYm+3jSNTghjqrsvi4hLJOMSexpxou2xegz23HGX7+jUmUYFp5jFCxIRxzN52B8/xaNWbrK1dQwqBU/Po3eiRuYLlYyd4dW2dqiO5vL5OirbGvnjlIrfcsswj332SdOcaIoE/+L0/pLtvmUZTF+ZP/dFnYG6JivT4hZ//Wf78k3/IOIh42yMPc3D/YdI4RWVxcYjJssgEQUAchuSJmQwksuqRxzF1z9PYk9DmeoaUmRWLI1u4RJMYLFWEAUviNEY6EgQIKRBSECc6X8BQhCpVt6RGGEjH8/3vSOz3X8Wo8jx/Kc/zu/I8vwv4rqL4/BE6iebP8zy/FfhzZsk0uwNIfxIdQPpffllWOftq7ESD54bsaWZqw9vYTeE3W8AoDDXJrbRaNTwiged7VKoek2nENJnxOcwDudta1S06ujSOmU4ioigqrVgHg2EZrqjpB+bEmXlZmcgh3/fL92TIlTqZJio3hED5sw0uZGZ+01WZTYne5umu0i+My0xXZYBSo2fUqTdReQ30hnPmiW2+lud69NR/no2a+v2K8kY25MClpSVuObhMreZz+LajePUmh48d5ZaVA/gVH3fhMN++ssV973gXjhB4cy0m05SjK8dZnDvAT/3I+/jJx97NeBhx/dVVhpH2jLdzRR5pMLbZbCKE0FmLQYBX8UBpm2mvAmkcMxnFqFjSbLVotVtYQiIdLauxHL1kqErFh95zHzUrxBGKuaqPLyXb1zd504mTVByXYb/Pwf0HGA2HVKsan8lRLC52te7T81hbW2NpaYnBYEgcx1y+fJkjRw7jCrCmAdGwRzQJsNAwghCaGKzZ/jrdZWtri3e84yHaCy1WVnTsfGIpXrp0gc1+j14wZM+eDsduPYEJUti4fo3P/fFnOXTLYe544ylyAWfOnOEt9z7AhStrCFd3bKOdHv/2V36NME45dNsx3blbUKvrzqhe3Z21N7MYNl21sfwxHbUZycxySN+T2gE0nATl3wPK5HKd2afKe9Z0XUbfZ/S6M4vjQnv7Xy0Mu0rEd9J+WZb1TuCf5Hl+v2VZLwEP5Xl+vUih+VKe57dZlvX/FH/+/eLvlP/ff+77njhxe/6xj/9WSQJTKLSZpyCNtama6TLSggk+y9uLZinEBQjuSv19SpA10sXAcWYNpNHYyaINNevY3aLivNhuBEFwEyZmrI2llMgiTst8wEJ6hXe6TqsZj8OyiARBQK1Rp1Jxy1W60UQZcz5ZnGSmMAkpygJTqWjmsiisbhwhmKq09Kve/dKsfs1jMddHLwpUeR2FJXGLqDDTcc0sa2bG/3mueOXieQb9Hp/+o8/i2i5pOORAu8nq5Qs05tqcvXCNyGmyvb7K2+46wRsPL/HMM0+TTmGw3qO7p8V3v/0U6+M+vfUhn/3GaQapxHPqdFp6+xbEOsHHjO0iV7RabZI4ZTKJye2UJIaq1yRNFG5dIm2tHZtr+fT7IZYtaTqKRx9+gIX0MipJsTyfaRjT2+7TaDSxhUs/HVKvtRkOQhqNOvPtJqefPcPi4iJhGLGwt0s6CZhr+Kxt9UtMc319naNHjzLs9wgtnydOnyN36/R3AnKVMhz2ObDUpb/To91oceuhw7z0wlmi6RBXSup+nYqUON024U7A9c0tqnNNdnp9QLC0tAQo1q5e4EN//f2ce+5JPClIconvCs5fWkPW2tTmW1piJ2FxeZGa51PvdPmBH3yMKI4RQLNWJ4vTsh0xPKg4nelLXdcljCNtw52kqDQt/adKLNWSZCq+iSZkoIndRWs3R8ow1s19O9MxzswF/sZjP8rZs6/Nivg73fo9Bvx+8ef/pgBSy7J+0rKspy3Lenpnp38Tb8kEOZjq7Diy7ApKR4SC6Oj7egTKEv3h2JYoN1rD4VAzbX2PaRLPzPeUAa5n3urmNBBoj2hD/4eZR7rhcY1GwxI7M3o9I9Y03aAQ+qQx5vmWRUmyM8kcSZKWzHfLEmUgp8HTdvOCzPbFbHBMsclzCkua2VbGdIH6+1Iwg6MSlzM4gW27xFOzyTH6w3RXkVKlmPv48eOsb6xz8OAyYRjSGw8JwphWq8l4PMTKBeH2NRqez+nnzvDss89wx5ElHCum2vLZmgR87dlz/MWXv4StIn7ixx5jccHHcwWrr25xYxCVN/jBg8saSxGSYb/PJAx1nFoiaPg+Vh6jsoA4SZlMI/xanZ3+kMZck0qtzk5/i+urFxhGYHk+O0FEb7SFrEqiNGYwDmk1W9oOyHFJpjGvvrrKkaMrhKGOlk9TvbW0LFHk/Pn0+31qNZ9XXrlAZ87HyULiUY94GiEUhfWPpD8OysPl9OnThGHIo4++G9/3WdzXZTgacvWVyzQ9n8P7D3DiyDEAOgtLrK5eo7ezxXsefRcf+uCPUHF8klzQXuxy1113keSC672AwThCpdqEz0oVluOy79BhvYSqejRbLU3DKQqGIXsaDMk4GhgfNtPRG+b5ZBKWjruWJZC2odFQ3hOeNws7me5yBTHPklkOma3gbAspKG/O1/h6zYWqSKB5L/AHf/VrRXz7d+QX81dz/SRg5xo0s2K9znerHkmh6dN6ORevqMpmxZ8kMQhBGEVI18XzfYR08etNHfSIII5iqhUPlCKOopLCEMcxliNJch32GRes9GQaFVolodtdAdM4ZBLp6K5GzdfxSxZYUpMtE6W0M2bRTpsTpFrztSspijidpfaarYhmq8/Acqeiu6gki6k1fHJb4wVRmpJZAkcIZNEdTdMYlKLVbOIUzotKmVhuMwYa8ajSzPVcByBMogisFJWnBVXC3WU2OBNX6xW0Ik10eAQipbOvjd/ocObSJq3mEi3b46d/7IM8eN9dHDywyMqRO3nxSp+Xeopf+Mcf4aFHHmDxtsPECA7uv5NRLLjvthO87bYlGG4y70OtImk3WlQcWF29TL8fEwO1al1fx6ZPreYxDocIGSPdFOIUkab0N7eoCo9xIsinMSdvO0qjBq9u97l0XW/35hdW6A0ikB71+Sbr1zbpLnRYWuwQRwFes4U/1yZ3XHLpYamYxcUVbmxHLO3tcmN9nX5vi1bTp93yUTXFoYNd3nDkKOE4IpoMGfa28KTETgV5BCvdJRqexG9K/uxPv8jmYAtLChzh4iq4vrHOxWvXOPvSeVYOLuPlgrSicL0UOxryi//bz7O9vcnW9pD+q5t8+otP0dp/mKX9i1gIAgXYLrd0D+DUPO4/dQ/pNMbNQBWwSZjOJGiGomBbotSIVis++TTFsaTWo1qzA1BKqeGOOETlZms+w652b5BNtwWU0IJRRZipoaQ4oH2u/pMx4L/w+k46qu8DnsnzfKP49/+mANL/5CUlSggyC2TF1b7KaYpbXJQ0TZlMopLzYSK7zUUxeJNZ38PsQu62I/6r8ep2jlZ35+DJ4vSQs1hrE9HuOC5zc00qFbdk12pcQ8zIl4XezhRSQ4nYrVM07bQZB4fD4a78QFG2ykbGYmWKNIpwLIFbnI4zE323xM7MKGpkHOZ6mM2iLMdnOQuS3BVlpD9HUZ64RnxqileSpNz9pns4cuQwvq/V9keOrNDev4RV9fn0n3waP42piJhL1y6iqk2efu4c//if/nNurK8xWrvI5uY6w2Gf558/y2c+81lylfLw2+7k+x6+myzusbkxpFppIyyXW48ts7jQZWenzySK6I36BEGAhXaG8KtNbAlYKdKBSRSQ9deJhkNWblnhw//LP2TPfJOlbhtPCkaTiJWjx/DqTXJb4i+0SB3By9cuM7+0SNVxufzKeVYOHmCu7lPz6/g1l+7eFlJK9i0tcvvtxwiCIfVGnQOLy4wjuHJti3tOnbqJ4nHLLStYNY8Xr1xkMI3x2m1qcy38ap319U0maco0h1anS7PVZBwOufbqKoP/j7r3D5LjPO87P939dk9PT8/s7Oxid7FYLJcgCIIgCFH8CVE0RcuSrMiOTdGxflmJ7MS52E5Kd7lz3V35cj5fKkkll1TqLrEdxefYMi3rVxRZsh1HlmTaliiZokmKoiiQBEEQBBeLxWJ/zM729PT09I/74+2nu5dXcaAqJ0VPFYvk7szOTPf7Pu/zfJ/v9/sEA1pNxV//kYcgjzlwcAnP95mZmaWfJgRZxvr2JlEe05ny6PgecwsLTGyT++67nyiK8f1ObfK0PhRl8ned4yQHfTklOgzZ2wtKLpo8X7hRsqb1tOmKMF3xAM1CWpOV61KuiRze9ZKwvk+v5fHdPPv9VGUfwO8AHyr++0PA52s//xuGfpwGdv88fEoeuQGGpVkok0yXH1EYQSHglfIGKANWlumxTKXOLtebVlrvaZqVfs56NJBMl0lK1rshVi/juMhIsnKjC3Avp4acMgIUSpk1GkVlqddq+WUKLYJMyZhkcUDlDT811Sk5T3WZjXw/Mko/H6t0YqwcFYHys41GYZlei3RHFoYsImlCjEZaL6iUwi+AV+1QIWZ+VcCybb3YXdfn8OFlut0ufttlc2eTrz3+OO35OSy7cJbwHAyldZhus8P5ywMef+YCSyvHODDXY3Z2loMLSwx2Q37gXQ8yZSve+T1303ZMLBXTbvvMzs6xunqRne1tmk2XzADb1YdFp9Pl1pO3E+zFTCYRk0lEo6GAjLyliJ2Yj//O7/MHX/syvSmPcLfPrcePYRvQ9T08R3Hj9St4rserr1xkeqpLOknob29yZGWZYLDNztYGw2HIi+fOcOOxFaIo4tKli5w+fTeLhxa59dYTXLqwxpNPnmF3L+LRP3qE+fkF5ucXsG3F2bNnUY7i+M0nsB2P9cvbbOz0WVpcYhhEBOOEIzce56VXLrA76HPj0SPM9nrESUgSB2xfOs/W+jqXr26ztHKUH/ux9zEIYqa6s3S7Xab9Dlvb60x3fRavW2JmeZHrV45gFzyn0agykqzkYRSyLf3/MrhBmitA6Y8uzwPtX6UpNOK/FpV4p14r1Sg6OfCgojtI9SIJQ55T6U//orV+hmG0gLcDn639+J8CbzcM40XgbcX/gx5Aeh49gPT/BX7mGt6gtIHANEmhoO47ZBPBeMwSN5EMSbpkQl6UmhgoSysJSBJ0oNIQjka6U5gW1hR5Oc24MswTxrjc8PrNEEM8cRsVfsj/j/1e6zIKFiCOCPL3xPpYPmPZMUkSWq6LkWWkcVIGST1kIt53solIVK6VpPGio5TupCzUkrgaRWW6Xw1erZuhFUJn2+XUqduYm5tjPI5YOrxADMwtLtHtdrh8NaDZ7nDTsRUOznfJc5PDR09yNVb0Vk5x99136uddXmNra5uPffSTbFxc47GvPMq73vFODDtkbf08o1GA47h4rna0NJViFOvDKQgC/uzxp5jEGco2eePttxFPIiwFrURxndflodMnOdnxOXLdMn/1B97Bbn8TK4vpX10njQKee+Yp7r3jTg50uizPL3LHyVPM9Lp0pzocPrTE3IE53v72d/Cud72TF86e4aabjnP99St85atf4cqVNdbX19jrBzz//AW+/6/8EIZhsr6+zsbGBnNzczpjTzKuvLqGYzicvv00iQEvvXQe13VZPrLCq2trZMUaO3v2eeJxzOHrlviHv/APuPLKeUwj4+pOnysbG/z2b3+O4W7IeBgx3gswk4S3ft8DzPZ6bGxuctd995GmOiM2DRlXlZRrUzBZwY4kWEhDSe57r9errXmNYWkdoFliq3LASvUiw1FeezjX/abqE5hk7QHfldbvdWFFfPLkLflvfew3yk1RdxCQVBQowWxMME29odyGp7GWoswbjULmD8yxuzsoAXkJbo2GllZI9iAnivjzWJZC1WpqpRRhHNFq+dXFz5LycyilyCbCdtffJU4qsbEELs33EidQpzTzM4vALAMZ5T2FliGEz7p0ITcqsDLLMsIgKMXFUmamaVZSGozXpNyymISsl6ZJ2VCQoCeZmjKrLmmeZ4xTTVt45ZULnD17lm98/evsDULmuj4Hp1y+c+Z5jCzhxpuO88WvPsYkgXEYk2X69aeun+Xk0WXOvXiWOFcYccYwHHDgQI//4e9/mN7iAv/Tz/0CX3vyDFFmcsDvsTcMyUyTG288xt5gg2AvJNgLSVPw/Q5BtE2j6eJ6Pku9Rc6ePcdMO+GBN53k9mPLvHz2LLe84U76w3XWLm1zw/IxXn7pLL25LpubmwV4btJ0FMMoJkkzbMclSSNuWD7Oq6+ukjUS/vbP/AxPP/44V9a3GWcev/zRz2E1PHZHEbZnMjW3wM6ldXzbxXIdZqd7vPLyBfIsY3a6B47OTC9fXidJEq5bWqTRdLm8uc5oHGIok70o4R//7Id59dvP0O44PHvmeZSrQfzIctnc3ubEG07y7nc/xCd//dc4fMNRTt//VhYPLeO6XtnwyakmEgteKQcUwDiLtSFeFGlPt0IDKLiqaZhlsAPITO0c0VCKbJIwoZKWmabm/tVpOrlRQTJ2YVuklJ6HMB5HuMWaft+Pfei/Wtfvv8ojTdOyVIOKc6E1dp4mqcXVnHsBekUULBu50XBotzsMBhVmI2Uf6NJoerpbvq/WIJnlxhZ7YLdwPByPI5qF346jHOwieEr6K1mKaBLr/C7BCASnarX8MtjUDfElcFTpeVXTy/f0fR/b1tlbq/DbFvxpaqpTptb1LqmwhSWzEhcF4biIYFTY/3UwVE7djEz7XxsUtjkupqk4duw4YRixOL9Aq+1z4eIa99x7P9dffwTHdVhfX+ONt5zg8MoSmcp4w62nmGv12IxMvv6ts/zkT3+Y9zz0Q0zN94iSBFL45G98kl/6uZ+nMwEnAwPFZhhi2IqplsuV1QtcWd8syr8Ot9xynIataCiHURCzuxVw5qWzKNdlYnZYueU+OlM9RqOQP/rSF3j10gZxkvHkN59ibmGRZ759hgNzi9xw9DiTBN72fe9DWT22tiI+9BM/yRvf+ABnnjvHvffdy/e94x18/rcehsTk28+t8y9/6ePs5QlveftbaSgTO4EkCDGSDFOZrPc3efbF57E8h9Z0h81Bn7Ur66RA78AsM3OzXFxb5aWXz9PtdjkwO0ur0cFXJt95+nEuXT7PH3/lUQxLcfi6I0xS+OF3vo3vOX0nvjL57U98jNtPn+bEqdu4/shRHEdnKFJp1JsidawyiiL29gZFJq6YmemVonipSqRqkT2VpglmruVbGWDalZuCeKKJFlAgDKDQ5jplpiXdacnc6zKza3m8LsZl/fIv/9IvvPe9P0qSTIrMaIK4/0nrtNFoAEUpkqXFl7WI4wRLVVnDYLBLVgQ8pSySZFJSH/I8L3RJaZntQAV853mOQY6M6bIsXUcbQFY4EWDm5Wc0TQtykKZnnufFsAiLyWRCnudlNthoNIiiEaZpkWUpzWZT806URTVVFj09ubCAMQzIs5wsS7FtmzzPyXI9skiPHjKxlSqlCI5jk+cZzWazLBGVbeM4NnE8Rux0IC/Tbz0mSbgv1eGm3UVTsryYgKsUaZKSZxnRaMyRI0fYuPIqVzd3mO7N8O1vfYu5qRmCcI/+zhZznWl2tjZI8xzLsgm2BwRZym445ktf+jKGaTGOI8ZRxPbVPskEVvtrtBYOcO/3v4NBrCdY7+0FDMMxvekZhkHE9tY2hmGwubnJQw8+yAsvnKXltckzhd9psrO5SZplPPnsd1ianeKd73wHg2DAHXe9mYOL81y6vEbL6zAYDFlfv8r29i5g8dTTX8ewUg4sdPmTr/4hQbiHUjFPPPWnvPHue3jumYv8y3/7acZ2m8xpMibnzPPfptVymZ9Z4PL2JsqySCcJrakOpmHS9lu4quAQNRsEw4BhGDB7YJYoneD5TXa2t4nGI7pT0/zsh/8OTzz6J7QbTUZpzjjJ8NsdWr7PxsYG/Z0d2u0OMzMzOL1p3vLA92JYmmaTZ3r6NkCeG0W5Zxdr0C4MHPX4qvFkjLIssjQjS1N9b4uxbnpEnIFSNlmm94ltKpRlMkkmYBrkWUaW5UWW5BCPtRxGH6w5WfFvwzBqZV/133E0AuCzn/0cf/fv/b3/81pixOsio8oLXwnBnfRkFAkuWVmq1bGiRkPjQM2m3nCy2brdLr6v/YkELJaHtFuF/S0/K0tKKIF06WbkaYZRgO51MFuyLzkxBL+SKTjS5RBgUnyABOQOw7D8XPVOjCZfVt0W4XCNRrqZMB7v73JKRilYgcbeopIwOplov2vJ2uoTpeUhRD0J6FIWp0BKRm6apbVJlumy2zA0t+q+++5jLwjZ7A/Y3tik0+kwP7/AztUNVnqzdDyX85cuktsK24AYh7HyeeSxp+n5PtdfdwSv28X0PN72Aw+yNL/EXUePcp1tsnn5gpYsmQ6rWwGzM3O0211d6lrwqY9/kmAwwDZdfvlff4SDvVlUGjGZaAeDX/r1z7E9Smh0Zvnd//hFPL/DxsY6Fy5c5O433Uur06Hd7XL7XXdz9z33c/TG25idOcabTj/Ie9/7k9xxx1u58eh9/G//y0f4/CPP0j18nLPrG2wHG9ywfARH6XFrq1ubJLbJDTcd03YxUUwexexubGPmcMPhFVodn8wAy3G4srmJajh4vk+r5aEMk52tNZ5/5ikcQ5GlLocOLTI7O8uVq5sEYchwnGE5Pnthwj1vfoA73nSacZqBqRiPK4dNXW6p0tJFN3vCfVm37BVZu5J9yx6QhozgoBTd9XEcMyxKOhH9SyNJE6qdkrcnDZ1JIXcDSq6hVCzfDY/qdYFRnThxc/4r//aXSjZ3klZWvmmaYJiq/LJQlYYiHq6GLEgmVZnpCVdJXmcYOugApSGdYFlS0gnFX9JToQ0AZIUxvZSUyqjeN88zxgWICcUNL6xd68ClBMc0zUqphoCUEpyl21iO/C5A/qy42UApnB6PY7rdri6PqUZnW5bJcC8sOjzaRLDeJq6bohmGSWrpwOw1XOKiPJC1lGVZqcwXhjsOfPYzn4FJxoWXz5NNMmwjo2ma9DfWUbbJ7XfczpsfuJ+t7W3+15/7eWZnF9i40ufQ4hKvXDjLHbecYmf9AlfXz7Ny9DiTYcChQ4s0uz5f+toTqO4yq5e36foem5trxGNoWC6Li0vs7m7jNEx2djZRtkk6zlhamOPd730PH/vUZ1mc7zKlTG45usjtdx7n+utWsC3F53/7cxw6vMxgb5uLr6wxGZocObrMuZfPcfHSOmnm8sSLZ7CGCT/4ww/xyJ89RhzFxX1MMFCMswzbSDh67ChPP3+OWa/H+mANU5kc8HpMdXoEwwH9/iY5GbO9WWbaXTb722yOAtxialG7re2DlJlw03KHbC/CNDyubG3TPdDDcrQHWrvT5dSdd7K5N+DmEydZOHSkPLhN0yxFu1EU0SzcYAUMt4XMXHSp5SCXe5tnlSe6bSuUbZbNHqgIxuKOIAC94FoNuxoMYVkmefFaCYgS2PReqpQPH3j/tTPTXxeB6uabj+e//mu/Ukb7Ot0+SZJyrlhFRqTkawCl0LEuf5ENKY6ZkvG4boE51VwzBex7rRVylmUFNyouTxvTqv6+4zhEw6jsrjmOo7uWRacxzzUTXgJrJUGoFoYELfnucRyVrHf5XHWJjVssFnnIZ5VuIyYlfcE0TaKw8sYavWZkd/1Ay3OgGBJplvTdSkuoS+5qso5pmgwj3TV9+OGH2VhfZzQMUUbG4nSXnY11gmGIsuDAtMfMlA+2xx9/7XG2Bgm54bA7CWjZDj/x136QJx/9MnEaMx5EHF5a4ac+/GG+/rVH+M3PP8ILq9uYtovveYyHAYfn51i/vE400U4ZMzM9trc3mT24SP+KLu22tgMMEg7MdIhGA/7Wh/4GX/yDL/D3fvpnWF5a5qv/6Xc4vLzEww8/zF4ModlhHA1wG6Bcj1e3tpkuAOqrwYAbr1/m6tVN2n5XM/P3YpoWGCZshzHTbR+/49D0HF5+6SJpnrAwv8R0d5bd3YD1Kxdo2Q7XHVoiiiK2g0G5pmzb4V3f/1a+/y138pn/8Gl8v8feYECcJqwcPcrKDUfYDSK++e1neeBtb+PI9UeZ7s1pY7/CHUMVh3yeQ1qoJ2QdWLUGieyHCmjPEC/0ShGRlNm6Xn+VtTNQ0nxKsXuhGBGajGFV2lzTNGm3q8Er8jmSJOEnfuJv851nv/OXK1D95sO/VgK9IuoVMbAUqBXVn5LeL6CfAICSfta7fZIpCYfKyKtST54rgsmpqU4JDgpWpFNjfV22lnAAACAASURBVGYZZmWRYhgmpJRBp9HQgUr+VqPhaueHIhuSTorcMMmuJEOpk+SE3lCfYwg645FunVwref54HGPZlY2yTMGBQo/YdEudlZR7QHkdx3lhKxMn2EoVbJGaBrIIpuV8wqLTun5lnefOnuXsmTNcPH+W2285wUsvnMFtdtjrb+LkCYoE1++QGgq/0+Py1W1sT/HYE0/xzvvvY6HpcHl7g2QYc3Wzj91ycfOYifL4/a8+TvvAIgdmFzh//jyurcuMOBxw8OASOzt9DMNkOI44NNfj1YtrtKdnMcl44C2n+aM/+mPyPCOOYuYPzHLl8jq216HtKjqey7m1dbrdDjcszuJaGedXL/KBH38Pf/KHj5DEMf/XP/+n/NIvf5ThMOTWW2/j6W8+w29//VH+ytse4FtPPsXhlaOc/9YZHCvhrrtu57kXz9Mf9dkbJByYXaDT1g2cnUGfPNazCVvTXdI0Y2dnG6UUozjk9KmTTLV1aXjo0CLK9Xnmuec5uLTMXW8+TTSKueWkpofkBaxQjYZzCrdN7Z8vYvYoiui0OyUU0Gg4+5pBUFFSJHDJPX8tjiuhQkTvvq9VA5OCtlDKaQpelSQTAunIPpOO+Pve+2PXnFG9bvyoxI9cbwT5ogV+Y1RprOjzoHIekE6ZBATBs8SaWPSDUET04u/KaSQlovg2Ce+p1eqUAVFuXpolZRfFNPXUGN1V05tWLGSk3LOK4FTxTarvW30GB8sSWoZZ44lVXUKZZqNpGcm+bqFIIxzHwbKrjqnneWTFOC6NN1QLVJjx4nUdRRFup3AxNQV7U/uCYVbD45KiW2eZisXDy1zZ2ubEyRM0HBPP97nhhqNcXF3FsBziRBElGRtrG0x3OqxevMDMdJfjy8ukJ45w5ulnaN10grNnV2kYYKmMXqfLaGCyNNfhya9+kbf+wINsXY5pNj0mpsLtdJly4OrVDWzlYZkmB7qzRFFI7jpEpsl4uMkff/0RLEsxd3iRMAhIk4QwjfjpH38PjUmMHcd8/gtf5NZ7jqDCADeB+dmj3HX0FOHFTQa7m3znjx/j1LFF9vZCLr/8LG9641H+2T/+OX79I7/IzW++jTfd+wC7D/0gzz39OLaCqbbip/77/5HPfOazLCws8ov/+hdpN+bodHxG44jdYcBoI8H3fXrdnl5LOJx/ZY2lmQ5+0+HSep/WlMnRG2/DtBwSQ3H73bfTbPrEcYaZVxw9PQLLJ4o2y4xKuryNhhbJ6xl+Jnt7QckKl7JxHBVDRsr9ooOWUGZEvSBrWHBO6fglcaX+mEz09HEJSLpKqOAPed/6sN1rebwuMipxT9DETHH3rGxLoMKXlNJTPKQ9Wo2FqrIkz/VKFrZp6mkf40j7HekAQcmmneQ663CUQ1IEFqB0FJD3kMAQx7FmS9cAbUmjdUu/agpIMJKMTDJGkdrUpTWS/dXT4yzTFjaCJQluJaWxPEfS+STRpFm5jlmmMa0ySFtVm1hsP+Q6C+8Kqmkutu2UwU1jHjFgFl0mPXNQ3t+yTP7kq1/h6voqf/iffp/3/ehDvHL+AsEgINgdEI1C/JqwdzyOaSi9McBkGIQ4mMQFxpaPY7KmYu/KBkvLS5y+927+0b/5KJ63wE4/YJyE9Pw5RsE2kWGimj5EIcoGx3UZDAbMziywvnGRqa7Lu++/m4uvrjE/v8Dttxzl0ce+wkyzQxpm7I0S1rYuoJSD47j0dwJW5ru07C5XNjexfMgsuPXYKV587gwxAd/7fT+EpeAbf/YYV9b7jKOAn//5n+cjH/kIlmVy8dIq7/ngT7J68QL9q6s8/fxFktwliCJyC8ZJRJ6bxGMdZCZhSLvdoeH4eqpR12QQhNx1z93MLy5y7z13M44S2u2uPqCyCgvVDiFhKVx3G4VnWavAqrKKOqPXUUVbEKij1fJKWMJATBqrdVtflwKblA2jiUxqKppGbjWyTf/9ag9XwveMH//Q3/rLxaMyDGMfY1VO7brSWvhI4pVUAcbV3Dq50BUHRAcrIY0K7V8yEtMsJtkaFcYj3REtbam6bnL6NJtuiVnV+SAVg71yWZBgJgtKq8izEluQzKc+OUZ4JvJd6vwryYTsGnhZUStkTLwqSzkpLSUbk3RcPpsETMNgn4WyLCTBBQUAFZ6MlMsSvNM0YXd3wH1vvo/u9Czv++AHOXjdMlPTPYIwIBxrgPfi6kVmDswSRtoMcbs/YGtbs68bTY+d3T6NpsvG5gZXtzb1dU4hHEZ4TZ/PfvxjNJyYiTEgszNCI8Ht9ZhqdcnHCW67x8lTp9nth/R6PbZ2L/KJX/9XvO8d93LD/Cx3nDzGyqE5nn7yCdrNDrt7Ievb2/T728x3Fpj2ZnEbPjgOYe7Sj7W+bnuY4TouT/zZUwQR9Ecuv/uF3+ET//5jejS96aCUz8/+7D/g8uU+y8vH8SyPL/ze7zBzYJb3fOhvMjWtGOxt6MEHlsv8/CKddpdut4NhwMziHN35Wey2onPA58iRFd7whlPcePQ4S4eW8VwPZSmytLLOTtOM3d1BKfuSgyaOY3zfL0tC6WRL1SFyKl01eIX7ZjVBXLCq6eluiZEKz6+uDU3TGskzz0qismTves1X61x4ewL0f1cx4vWSUX3633+ixG3kxJfIrVRlsyJTLySaQ9Ulk/LHyCu/qckkxm06WKZ4lZtkWVJuttEkwsTEK9wNJ0VmIplG2T0bhszOzpYDQUUXJYA36BuUG+wD+SeTpAQ5JYMp8S0gjkU7pZ9XH/duGGhXhOL3ErAkEApgL3helmU0i4nRZUOgCNoC7MvtFonRZJKU9sZyCsr3kXS9Yq6bWJYutU1DFR3Q2n3ITJqew1e++gjnXzrLHSdO8MiXH8EExiNtSzKZxGxtbTM3N0ewu00QhMwdWGBvL6Bhweagj9f0cDFZ3+lz8sgRRuOI/mCbm0+eZJTHPPfSBS68uobl9pgksLc9YH66x6YNtqHob13kwXe/jR9/8EGeffxxrrxygXh3QDBJ2NoJcGyTURgzCAJNNcHENn3W1ldptj3cKZ9REDEK+nQ6HVaOn+C5559h2nO180GrwygdcHB+ljPPPM3S/BHshl/a/7z88gX++gc/yB/8we9z/fGj3Pe9b6M75fETP/lhDh46ymZ/G3+qQ7+/TU7CzMwswyCk3fE5ffpuVlaWGcQRc/PLzC8s0+52aUBhJa0Pn9EwKtei7/vExbRrz/MwDcrAopSeJCOHFFTTuusVgXS6Gw2XSaxtgsRnXa8zyv0nKo/RSNNeDLHFLqqNUTGJSSYy1RtIFWxi8mMfuHZm+usiUN1yy4n8Nx7+tTJj0ZyhyoFQorNsXsdxS0mL3lTZPj1bntb1gJCTFH46qggQWQlg55YW/SrT1F7tVI6EAvoJUG7bDleurNPpdMpgIBpC6SzW6QkCOgpvSVLn+k2TxSNBqOK4FKTLOCmDoUhtZMCFvFZKzGbTZTiK9qXhquBmKaUn/NQBe8k0oyii0+mUIKsA5s3m/hIaChpDmkFuYlrUgqeJrVziScj0TJff/b3PcnCqy6Df56t/8hVsSzHobzM11WVtbU03J5KE2dk5+v0BDcfFTCMub21qYHgvZDhOmPU84kRb3rQ8RW+mQ7vT460PvIv//Z//EzaDIqhPYpI0pu12eP+PvoNhsErT9rlwcZ0sN8mDPqOJye44odlw2Lu6jdFQHDy0yKsvnmfmwLIGhBX0dzbpTHcxkpCm7zIYZ2SdDvffeYKXvvUM65c2+Dv/88/xu5//JMYkZOngIt/89vny0BgOQ5p+lxeffYre9CzTB5eZmV3gK197gtxyyMwEp6nlJF7LQSmTpQOLrFy/jO1oB9e5G47guB2mphawGx5tW7G318cwi9Ip0W6o0uxRTjXd26BqEjUaDtG4OlAMw6TZdMpKQ8z06o0qo7D1lkxLOuYSyKByAQGIC+BeDuBcGj+FTRBUjSPh6aVpxge/CwnN6yJQnThxc/6pT39Ci4QL8aSYbUnAgKq1qgqQVyK8TB2W50u5Ju1VzEo8qR1AKzdCo0bxzzLt5Sz6N9d1GdbcOqVjKDq58TjCKIDJOlerzumq6/QkONXlDQJYhqGe2CxBaDAY0Ol0yFMJRtXJVxd7yvw/qNxH5T2bTT3HTjqWdV6ZLBY5aeucKhGkSmlX1zaKYFm+Q9XiBqj+jmGYvHj2WaIo4ptPPIaZZ5gJrF5c1QTH7W1czyGJ9f2Iiu8veN7W1jaHDy8TDAa0Wx7LS4s8//zzGGT0+30OHJhj/rplbrjpGKduO8Xnfuf32NvexLUU+Tgh3AtAKa5e1R20VzfWNU6pHJJxwlSvy+7uoGC1mthmxu4owHAUeZSgplz+3a/+Kr/4//wrsiTj28+dZXF+gd2dPqsXL/Ls6iq2AXfedhtRELC5t8m//Kf/kM999tMEo4Q0yPA6HodWlviTrz1KqzPHTn9AbikaTY+1q6scPrzMPXfdTbvt842vfwW/2+OO0/fyxjtuRxkeruuWBnbDICoPGaUUdmHlIp09WR+u6yJTYEQUnxiarGxkGZMoxikGoVScwWp0Vp5nmIZZYpOlgDinzJikeSXdaKk85OCG/fpXwbpk3clB/p4f/QBnzpz5yxOoJKMCalNl3BKPki6DlGFZIlQEVbpWit2EOAhATTiMdhYQN0OTKqMRQaVc9CjWUztKj6bCBE8ynIYtU2YLoLx4r9K1kMovSkq9+qaPonBfpiJgtOBxdVpGmmaoAmt7bTBpNNx97HbN5q9E0FLOWYX4WRahXCspSyeThHbbrwae5hXNQ05aYS5LYIQKE5TPIyaG0l0aj2OanrY8iYIBW5sbnHvuLFmSsLO1zeLCAhcvXoQ0I010Q2N3t1/yd9xidHocRRycX6ChFMEo4NZbT5FkGbfcehvpOOSjv/or3HR0BcuAvSjBa3r4TY9Bv8/uWLfQNzc3MVsO/e0BVgae7ZEXa4cMpqc6JJbJ7mAT3/doeV3+9MlnGQYBN6wcwTJMrkQBUaT1jaZpspfExFFMf3MD2zL5lX/9f3P55XOce/F5vvaNx+n5DtHExPW73H7XaX7zUx+n1fK5+ebjbGxssLK0RDTRlJHDh5dRrqa2vP2dP6gHdSb68Cv9yC2nPKR8X3cP5f7UJ26LG67O5CuStNxTyaqgMI4sKoz6YWoXB7lQFuQ1cuDJoSwNG0kAxAdLcK4wDMux90JfqI+i++CP/fhfvkD1qU9/vEw96xFZsixhb9u2YjSsgF8JMHKTBNcS4Nkw9CiuMmNyXZJiM1uWIp3E5d/Osoq9LTSFVtvXHJ1hoHGmsfZMkvdO0mrcVRRFBavXLG+KZDniuy4BQ594mWbiFwGrDrzL4siL7zwex2W3TE4lPaJIb6Dp6R5ZQfSzbUf7s7c8MiixtDiOylNOTsQ6ZiblaV0FAJQDTOXnshHkEJFTHir8zDRN4iyj43uce+EMa6sXWbu0ShrHbF3VrgVX1zYw0Mp829KM6EbDYWtLO1xsbm5wZOUI2URzoDZ2NtgNQqIJDKOIG5fnmJ3qkWcJf+Ud7+D8y+f48pe/zMGDC8RxTG92ju3tbfb2BsxMdxgOIwxLEYQhTb+HaUA8CvFbLtguSdzHsSGaKF7eCknjhNEwZKrTQTUcBsOAjc0NlONwaH6xoK3A+pU1jChiebHH/EwXr90jT2IGw4iTb7ybLz/yx4zJWDgwS54lfM+b7+WlF86RmSaDMODo8WPcfPJ2jh8/oak3uZ6dJxYreZ7RdP3ywNY7pBomKmtdcNCpqU4RHDRA3iyIyFmunWiVocrDRebwCbht2w4UGGkYhvi+Xx7kdVxTOtSyluRg193Fwl20eH0cV+4NMk8A4APv/yDf+c5fokB14sTN+Sc++Vv72vOiI+p0OoRhuI/dKiOlBCyXVmoV0Kpho0DJbZKgRyZgdIbr6HlknU6HnZ3tsrUqOJSpdJAB7bZARrkAJpMYp+GXm9bzPMZxUJJOpbST00xnOaoMEgI0jkZhmXkBZVNgMolLwqYEYuFOSZtf2s6SGWlDPI3pNRouaQ0Dk4VZt9Gpv5cETK+QYEiqL/5FEvDqbHghGqqCIJrneu7iaBRh2ArLyEgTbUr4p489igVsb2yysbHB1pVtDvS0HcpUW3e/BoNBaYMzjgMswyHci/CbPp2ZDqvrG+wEEamhmO8qrlzZJBolGJbilutmabY8sEwGgwFbG5slqDzrOuSWYpgmeN0uWxt9zTOLI7I0xuv0aBgRWRJgNnq8vKdtUEZBSBLHeI5D0/cwbMUgCBht9Wm1PXzfw7RNHnz3D/LM418njUPOvbLGiRtPkpDx9JnnyYAbb72NG69fYXN9jWbDoel4uG2PII74q+9+ENvpkGcw6A/odLrEk7CECQAc2y2znvE4ptX2CcOQbrdbQhUyjipJKo1rmmYFQVZh2ophHNFU2plWykNZk4K7Kmu/QWMlWq868PXsXdaWvm9CVK4cdgWGkbUlFc/73//Xr5mZfq3GeX/fMIzvGIbxrGEYnzAMwzUM43rDML5hGMY5wzA+VXiqYxhGo/j/c8XvV67h76Ox7KLmjWOMHDq+TzrRvKMkTmCSYecmYu9iGHozhlGkATwTwigsMSfbFqHkfvpB+T6pDkSOqze03+mUdi/lAIZxTKvp0W75NGxHY1JFyZcBRhqjLHCbijTXgVRq9jTNcJSjB05MEpoNF9txwTAxLd2NMU0T3+8UOJhHw3bxXA8jB9dxaU91tWeVZZaDKQDa7U7xvRJMU9wX4/J7K1UNFpXRXaNRVCwWvxRQG0blDCqvldJTyuO6m6lkg1B1XptNMSXUdI8w1OC9Y5qYKNyGj9Pwefvb3kWn02NxSY/cOv09tzPOI5yWA8pkM9hklITYrglGQm+qSxgF3HHv3YxVwtrli3R8+OBfeyczbsarr64yzkxSMmwzI4gTXlnb4Kt/+jRx7rG4vEJ7qsvUlM+k6bOXQrc3h0IxN+MT7m2jlEOSZRgq4eZTp2j3FnnuxYusnT8HE/A7s0QZhGnEMIrZ3tTdvpnFReIsI8kzbjxylI/+6q9iGYr52SXe89c+SKIUq7t9ZpcWePd7HuItd9xGEgww8ozNnU0GRsCtd9/JD/zVBxmHEAYh21ubxJOIYNjHtBRNz8NSikmSkGYJkyRmksQ4DcUkjphp+4TDAbvBAGWZpElMw1FkaUae6X+yNCEzTKJJwu5ugJWZTJKQeBJiGHp9jJKYJNdlnlVkaM1mNUlZ9poc7vVMWioEEcRXAa0ibEtjSw5A4QPmtWTivxgj/ksZlWEYh4BHgRN5no8Mw/g02sXzXcBn8zz/pGEYHwG+lef5vzEM42eAU3me/5RhGO8D3p3n+Xv/vPc4ceLm/JOf+q0SSE3iWtk2SXA9j/Eo0hM1sowoiWudNWg0XYIgKAE7Ez0RRNLMNK9G/MSxHvSgiaAZ8biiItRPjdd22qRTZ1gVBpBlGWYKSZ7hd3zGsZ48Kw8p5eRksSwtQ5FNPhpFNErnRR3gJuNqpt9kogMpVKC8Ms197HwBs7W9i8bh5FRzHEePTiqyn/qsQW2brL2+ZMp03Tuo7q7QaFTibWHoS/tZ5DzyszqjWb67cMRsWxsPvvjCGbIkYXN7javr60RhxPrlNUaR/v6kGZ7rsrMT0N/p4/sdlHK4snGB65eXysPBUD47uyGOrei2HPq7A1pTHfrDgGgcsTS3yEsvntOk14ZXNlR2d/sszGmqydraGisrK0TRNsowSXPFXqqIUj1ea5JmHDgwxzgOCIcR7bbPpbVVluZ6nLr1FEeOHOHxxx9nquEQJxmziyt845vP0Gwr1jc3+Cf/xz/k1RfP88QTT3DLG07RnO4ymsS86Z7TmKYO4uNxXNJipIyeJJXjaqvlMwrDsiyPIv05snFCnCegFJ5IqKxK7VDy7wrmuvAQm54qO7d5ro30fM/HyDLIqwOqPhZOoBFpVMmarDPOpTQtbb6Nah3J4SZZ+WQS84H3/41rxqiuVUKjgKZhGBPAAy4DbwU+UPz+N4BfQA8b/eHivwE+A/yiYRhG/udERMMwiuzIKXEoAQflAliWWVxERaMgVZqm5jcpR9Fu++WJ3+50i5Pe00C4MG6N1wxWjGMMo0prpZSUbkjJ9oaSjS7ZkmBcRp7RUIVNaw2oLOkSEvjywv/cFC5YMXWm1uGsA50l1SKvLFzNgkIRFot2MklqvlIQBMG+jCeKIqyiXNSM/2zfgouiqDzl9HXOam4SlO+vO5I6WImcqD4QVU5OfS81liU2uALAmqZJkmXkacaNN50giSN2n+rTm11g0N/mzTcc4dvPPs8oDGnYDmuvrnJpY5XpTpdLaxfptrscPDBHGCQ4DcXecICrFFkG6SSmvzVgYrkM90I2Xl3FskwuhLocOrSwyCurq2xtbdPpdFheXmbj8gYNV3HTTUfZ2wvpHVhib2eTw4tznLrzXuI04c++8XWOrBzl1QtrBGHIT/303ySKQp584hmefvIxrl5aY9gfEI9imr1Zzj53jnGjy/z8LMtHlvjp++/nN3/zYaYaHu0DXRLT5O7T9xKOEixTr/XBoBpXJfdyPI6Z6nYZjcLy8KEg9AqOOB7HeLZDnptYDYewMJfUfLfqHkVRhNvUhzbo7DnNNJE4TTKSJEM5BSUmSXCLplVdT7t/QIq+x9LMqlQbYJqVm6fgy4KpyZoW+3ClFIZ5TTEKuIZAlef5JcMw/gVwERgBXwSeBPp5nkv6UJ/dV871y/M8MQxjF5gBNv+z74E+Kfb2BvoCJ1mJNYHewOMoIhnH+C2f3Kr0QnJKCnAXBEF5cshrTVWJLTudDv3tftmFM41KnFnaodQChtAFhG/0WgzMNE3iiSZ6uk2XqHBkgOKG5lKng5mDa6qCMawBfM/zGAwGFXaViLdQoZNKklLykOdZaQoowafeTBDZi3RVlFJYxaKJCpsSAdAlg6xrrvSJvZ/sKUx9yZDq3kWGkZUBS8BSuS7VlNysBHkN28GkIKpictc99/HqKxc4+8IZonHCG26/jRfOPM/ltTVMW3HythMkUcLC3JymlAQBueHTHwZgdwiGMVt7AS0Xptse/WGEbzvctHKUcDBgK9Wt/W984wkWDi2wtLSEnlYdoywH24bL62t0/Fn2ooze3AKrqxfY2NygO71INgowRgHRdh/HVvz7hx8mHAVcvLDO8nXLzM74NNtdpqOY9Y0NDh85TpTG3HPXKQ4uLLGzo0mqKodbT9/OocUVhnsxru2Qpbr1HwQBo1FYOKhqjLXT6bC90y/Xc5omNBzdIJHGj2XpNZ6akBoVzCHdNw2yV5pTqUC63S6GmZRrOs/1gNLJJKahnFKjJ/dwPN7P7ZPAJaJ5Waty0GqIQNMd6h1vOcyERgF8V5OS/4sYlWEY0+gs6XpgEWgB7/wu3uM/93fLAaT9nZ2SyKiUKjRp+vRt1nRqTsslU9TYrsUfyzJsSxFHMe2WTxTHOK6LZWv8SYzu8lxPqTGVid1wyDEZp9pXXClNQJQLLAMWk1QPFNAe7G55WpTsc1Pbp5imSTKpRqpLBmTg0HI9XOVgpTrQ2EqPAsvShPEoxDI0c3syjpDBDwJ0GsIjyzLyWAShlWm/YelMRRWdnShLMG1VTvTRjPCkGMwqYH2C2BxLlikBSsrQ8TgqsbWG7ZQ0DpFjABimScN1sZQiK9UBCfuz4cqv3sr1vMYsA8N0iMYZh1eO8q4ffhBnqsPUVA9DmVx3wwrvft9DLB1coTs1y4H5BaI4obe0yCgfoBoJhpngHnA4fH0PwwHH93BVwtWtda6GAQP0/LypqQ6333Ubnu9y9coa41FAamVML3Wwmh7K6ZKkEZ4J2QTGYwcTnziJMByfx751hoCEvUnA1b2Q3O2wfMtJNsch7fk5vvTVr7C6ucGz586zGWzy4oXn2Q1CvvyHX2Tn6iZv+p77uf9d78Jz54jHaLDfNsnNhIyEZstleqZXOM/qALC3F1TNniJw2Y5Dw3VRtsIwTZIkZpwlkJuovDJmFClLXQDc8lzchsIy9dj50TAmnei1aBoZaZigckWeQjLJqNtXSxlqmKZ+37S276h4g3pP7xc7y+/qdB6BADSYf+2NvGsp/d4GvJzn+dUiwHwWeDPQNQxDFVlVfXafzPVbNbTidArYeu0fzfP8V4BfAU1PkJNYypwkSVCWpgU02prKLx0DidJS3onAV4JLFFeDPiXrkq7IZBJj5CZRwUFqFqlulmdERVdCp8xxye4GzV2Jogjf8cpOieBMrZZXpMIuo1FccsHSNMF29QmUTLSRfp0gWZ9GPBgMmJ7ulaVn6X9tOyQFx6rRcHFVZRpoGBrYFwxIKUWeJIyjCEc5pIn28gqGYVlWiGuCXmw6oOztBaUuDNiXbcpCq7hSSYWpTWKSib5n7ba/rxso5Yy8l+bT6Huvy+cMA5dkojVgd95+P/GozzhOWFtbZac/4MKlVQ4tLGBi4nc77IUh07OzzM3N0Wp5zM0v8h9/9/f4hf/zn/D53/4c4yTBdj16vR67uwOsVF/Xy5fX6cz1WFpZoWE7XFi9SByFtP0e7SlFZ8rjyuV1ZmdnmVtcIMsyLrxyEaUUBw4sYJkKiJnq9rh0eZ0jR48RjuHcy+scvekkBw8tcuINJ7FbHseOH+Nb33iC9/3Uf4dru0y1u0SjGHeq0lrqbFY2dlbCHUop9vYGWvQNRWctIAzjcqRZed9FjI8qyzCRnslDgsVwGJQlYX0NiDqj4TpkWUKSAsZ+CyWopiepAuJoFK+Xfar31v5sXd5biMymqfYFq2azwkuv5XEtYPo9wK8Bd6FLv48CTwD3A/+hBqY/k+f5LxuG8XeBW2tg+kN5nr/nz3sPAdOF6DkchrSaHpNiuMK4yB5KL6Wosl7Rn7GSuyileTLVfDJ9E0SvludAcSEdRzOjswI8yg3t9yRg9niseVFN12c4LKwybB2c6nKYONYcJ7HAEDzM6FYeDgAAIABJREFUth3CcYJtaUV6w9YseekIxnHMzHS3MN0PyiELsvBAE6fdYsCEkWUYqmpRNxoOaV5xl6TlTJaRTTTeEBvVmG6zWIRSDtSJgjqwVB5XpmkyHkXlojZNrYMUWoNhQJZkZZA2TR00JauSLmH9OsmjGj5ZjQ8DcGwTx9GSqKeeeoosT/AaLq9cuMChhQW++dQT3HjjMb7znWeJ45jHvv48y4eXePGFsxgZNNsOb3nLfShbT8ppt1wuXLigqRcmOJh0PR/DMtnbHdBwfTa3B8wfnGUcBdxyy0m+8IUvcPjwEg3XYRzFdDpdrl7dZrbn0Z2Z5Z7T93JxdY3vnL3I4uICu3t9mr7LjUeX6C0sMEnhhsUVtgaB7saaipbXIZ6EhXA4Kdv6daE4VNZDSZJgOzJ0ocJ9JLgkSUJLiMHov5Fm1X2VQwWKNRiGJfUnzzPa7c4++GJcEGPLhpFhFty5AiumypwsS5GlFW9LqDD1zKlu3Cj8wvpQUkkefuSh9/Dtbz/7FwOm53n+DcMwPgM8BSTAN9GZ0H8EPmkYxj8qfvbvipf8O+A3DcM4B2wD77uWD2IYlPR8ibqitBZdnwQXZVaTVyUoxLHmEg2HIW5xE+WULxnqRZ1t5ZBOElDaxrjV8hlNYs0OTmuYUsEKlzZtmma4nj45dncHdLvdku0u2URSjCesuyqYJmXb17CqG9poOOzuDvB9fx/dQhZLmibkRSBURZ1ftY8rTpNRnoL6Z82Gi+UULggUGKDrYBVnkpZeVBQO2Rxy2kuw0RiDCEpVWSKXzGRlMgpC0lRTSCZFaSkZbJ0YKv7y8tD3tiLe5jlYyiWeJDRcxW2330luxHzj649xYG6OC6ur3HP6NP1+n82tbRYWFmjPmPTmXK5LZmk2XBx7jv/0hUdYWJjj0toq/+xf/AJPPPEEX/rSlzlwYI4kjLjuuhVN2jQd2p0ubquL23FIN2NeePEsN918nJtvPs7a2hpnz55jqjvLoaUlbDthfmGBcRJz9txZkszkz558nO99xwOcfMMJTt58jO0gYBJnZGNY7C1oA7vCZ16aEVI+l7MSHa1BlU6bnk3pkqTVgSFYVuVJVvmc28rBMBRJWjlwyoE9HAYlv1AwJfEeE7rA7m4f04QwDMrAaNt6nmK/39cJgVXJpfIc4rHea8Ljk7Uq3caKJ2gyHidFJqgD7XAYFntJhqJc2+N1Qfi85ZYT+cd+6+F9wzrr7XETSmBXyjuRzgjnSTZXvev2Wl2cnBLye33qVE6gAHlSTRN2XZfU3C9SFmBQ26OYJche2czs/27yfiKRqcsQLEu7NkRRVGY6o3HFDpcNL1Y243GMV2BuVfclLk9i0zSJYk0SNXNoOA6jmseU0DTkvW1LlaJqbV1LEeA7mlsFNeBcfx/J2uR61w346vyqOjFXMmHJ3kQKVdcRAoS1ZoV2wTBp+Q5pFpMkEX/6p4/z0kvn6ff7XLhwgaAf8H3f9zaee+4MlqVYXFxiY2ODLNPOmZ2Oj2HC4uwsrbYeBXX16gZRFHFoYYF+v8+zzz6L4zisrKxw8uRJnnvuDL7v0w+0nCfPTDqdLpkJhw8vl4Fm4fAKc3Nzen2aJulEZ/SaJiOdZcquaUMoMUUTRAINFLMBim6ueKDVzRInkwTHrrzIPM/T8wdr0ATFe8kBX7nJVpBKnTYgkjTPq5pFkgkL/aW+loV6IklE/d7JnhCKjZT8nU6nVFLIo2419CM/8t5rJny+Lhw+c6qBDMA+u9w810C5BIsk0Yx1z9Ps56p1rjMj4WJJtiG41ngcF95TlF1BGcEuTOo8z2g0XbKCGZ3n4HluyS4HypJSKZ/d3X4ZWCXL2dsLys2mP3NWC3TV0IjJJGY0SjQRtFl12ESQXU/fpdvWbLokIqwuSgPJ+mSx2TmFe4Sk2BXvSz6nTtOzsoVcxzJEJ6gN1zKUqjJSx9GSHfmbcUwtA6toDxJ4ckQGFRUM98oOpC7JkXZ1XcGfZRlGnjEYxDiOYjLJuO97HuCuu+8liiLOnTvHiy+f4/z581i+ntqSjDLGaUKv12NxeYnnXzjDTTcf445bTuE2HR599FFuOHZMr4c4xopjDh05wpEjR5jp+lzd2sDreBw8tMgrT6yS53DLLSdJE7ju+qMcOXKk3GRRrEgTVVACTAxgNIpJU1CqGlsuQSct7knV7ldlc2M8jpjqdsv7LNinHLSO45AVuGjVjdVUElkHtpKD2MSy9PXzfb+8l+JaIPdKLFj0wVVNMrIsrYEUqoNwo8Rpo05RkH/rveWWn8eydLdcqBR1T7k6LGW89lT/cx6vm4zqU5/+OHq0TiUr8TxNRJRhDPIQQFewnDpwDjp7CoKgzHba7Q55npUgb520WK+rrYJ1LlNtJPpLYJELLmLoTqfDXuFOINmSnJTyfGnJQpXdycmjlCq7aSVZrlZ+yWkl/10XKWeZ1vV1Op0yQNi2w3gSY6KV8nmWkReZoud5msGfV2JRZVYcrvp0mjIzsqux8JUkI9l3HbIsK4mG9bH3tq1K8qq0syVDqL+PZI/jcVS6o0oglmujr2tCQjU0YzAY4Lc6+zKH0SQkTTOuXt3g/Pnz9Ad9lpeXmO10cIr7eunSmtb+LS2VkqPRKGS+0+O661YwDM36n+QZSjkMg5BWq0NUKB5ks2/vDPbdc6vAAaXMyrLqYJNDTgzj6u4ZJTRRBG2h24zHUVkixnFMsxgxJZlzUkifpDyzapkOiBNusK/jJgGzjqnq/RSX92BqqlsSpeXgEoqO3GM57OtDRIWoWqfGyHqv/0z2bp5n/NAPPcSzf1EY1X+LR57nBTBe+DfZDnla2fDGSYJynLLsiOIIT3l6LE+aYDccJmmG6agizXZKfKXRcDHJNFZjmGSGYlJY6DqWiTLEWQCyTLeNS31VkQ7XLVwAhkOdogsADlLa6dfImHA5fSuhceWiIA+hM0j30rCqmWyWBeQKUjDSBDs3yQwB7LUfkXJUqf1ThY7RsDQIrxynMDeDIMiIM22p0vF9bcivqpM2jmMctzDFs6pxSVLqCEdLp/ZFUC2WmCai6gk9G+vrZZtcmSZGYUhoIsMDqmYDaKxqMgn1YZGjR0M1Pe2CWuA2w5HQKFzSLMMyFe1WhyzPahhfgmk45FnMjSvHONjTU5xnZnqMxxGOXY2yD4KAhuOXgXI4DEtCZBAEmFZCo+kRjWIaDf1ZDNMEQ5ED47ja+IIBJYXUS9ZCZZ6YlYdVnoPX9Mlzk3gSlvc5CALiJMBvdRA5lJSHsqZk45dVRaS7zd1uV2dNgFmUZ8mkcqAQLlMQBKU3migMBJPcT40YYJku48J1tt32yypBzPK0+wf7DjAJxvJdBV7Q/le6QSZ4b7n2v4sY8bqwIpaLL/W7aI0kMAgOIqno9HSvTH/zotSZjGPSYuqKpM/NZo3zY1T+T62mp0/rmmxGTrY60A2Vs6HcAOEJyakvnRh5rSjeJeMTL2oZryWnstbZVdM+JHN77Y0fjQLSLC4DinTJ5Lmjke4YyqknnvPyeX3fx/M8ZKhrnXFcYRbVaSvXQa6bYExQuShUJYoqgXgpHWZmejSbrvb/LljJo1FIGIZlc0QsbGRQpQDGhqU7r1Eck+ZZmX00m27ZMatno3lOCTTrv683yGAwKDudL710niiKGA4joihmPE5wHO0lVpdNTU/3UErVoIKoKIP1d/eaHlmaQJ5h1Tpg4mIh2sj9QSqpZeb6YJEso+6kYVmaiCwYoWnq4CVrQaspKpGvZSlt9VK4hMjzJLvzPG9f5aAhDK/MZOoYUqvll11oKcFlfYlSYTLRcMvOzjamaZbCd30NqgxcvnPZLSy+qygp9O+zfb+75hjx3T39v85DRlHX+RpSn0t6KW3WrAAdhRzqOI6mK9iOLhGLWl0cBQTnkfLKthV7uwOUadIs3A2lUyFCTHHklBsqmIk8BIAW0qR0Z6DylJYys9T4QblRtXRiwN7eoHwPCcLjcVRKbOI4RtkmSplMJhHjOCw7cHWTOcn8ZMFJ5gKUJ6kEoHqZvN+jyixa1365+LSRn1t6sUswk4Amm7nZdEt+mFwnyTAlq5X7KN9Lrpec5lEUMZ7EDEdhmSUL700wRcOAfr/PeBztU+67rlt6hAv7XrzxxT6a3CQaxeQZpIXXk+A0cRyzsbFRBniBBCSjyXM9tLbV9HBUke0X91DWo+5mOeXakc8n5ZPoJYfDoOy+CY5Yp4BUB6UOzKLDFL6S7A1ZVxIwRQNoFiVuXetXP7xc1y2F91Iyi+uIrMGm54Ah5F5VBr76a+WQqvCyEPHaFz6V7GnZX7IuBfP9bmLV66L0k9NfXzxdesiGVkqRQdnuloxCFm+j4WIZaN9mA7JCKFllFRmWoSdhlKB3oSmMis6hdPDiOMa0NZisHQbqWJHah7FAlfXUpSuGoW1OoMYuNyqRp2RazaZOy42iftdZkcsk3X86KVW8VunOkry3bAQd/EQGUzUe5CEZm2maKFORTvQmaTXdopyuvofwZ+TzarpIBd5LKSgHRII+cZtFWRBFEkgLbK7YhDs723ieh2GpEniV6zIchsXC104Lk4nmtVk13zH5TFV3KsNxqu6hcHm8ts841I0W21JEccTe3gDP01nX1FS3zFQEt9ne3i7JltJ9tW2FKnhC0lkdBUG58W1VOVJIAG21vJL4OJnEZTkq60NKOANTKxzs/diVYVaHg57wUk28Vkohu1oyKwk+8j30PemU2tayG4gubUveYFJNJZJGClTTrzVNQmOZpqH3Y05SZq567SVl5inXTnyr5N7Xffjrn6UejP/SlX56Y5marWxo6YdlO9gNh7g49aVbIKcIVEpyx3VpNPVGsGwHU+lWfBRrUW6a683TsB0ayiSMItJcy05yg0Jqoy1ckgkoJVOUq4AkJ4+k0pLy19m1cnKEYYgMY5ATXsqJyTjCRLfu8yLrkRJRaBayWYDSijiMIuymBtvls1u24BZ6mm1dACr4h6kUe8NAB8CseK7jsFd4qwsZT8BQmeWX5xAVJv/KNCHRNsyWoYmgcRRhJhnZJIas8tSCGvkU6A8GdHs9zIIZLVN/BFQVSU6eQzpOcEyFbShGeyGO49Lv92tdowwZgpEkCdEoZFI4ViRFyec0XSZ5xjivJugEQYhhmQRhgOu5xElcGuilecbO7gBTKSZpUkqRJnnGeFJsWGAwChlnCaNCuhKNQsgz/JZHp92h3emgbEWWQ3e6R5JmJGmCsh0spcAwSdIEU5mMJ2FZekom6zbcsix2HJOm6xKPY2yldFMkl66qKmfwmYU6QFkmySQuStMq+9drtJJGyQEWx2FZ5lmmKjIlr8CvNC8LtA2SpaoZf/VEYTgMSBL9sziKmYz1P6NhyGSsbZqMHMjEcaEaSFJRVf5iJTT/DR45e3uaQCl8HJGgCHeq6jplJedIywJUyV0RWoB0RPJc84N8zy9TVOGJCK9KQHqJ+mIIVlmoVkNKpTsmDylL9OsjDMMt/wZQliAiyzEKXEwWp5Sz9VLMqrG6m03tRipBMi2/VzWOq/55skw3IMQiJs/BcR1ct1uezILzuK5blm5SMmBWjPo6fhLHMY6qhp9K10pfF7c4haX9XJUbde6UjA6Xz6nvZbJv4co9kM0QBAEzM7Ma4C4ygm7hKmCa2gEAKvwxB5JxXNgBQTyJS7BZMuqqjKzoEdpBVSbtODSbLmEcaUVBgSeq4vq3234NyxEto9aQ6sMmqnHmnLI7LBhsllXXz7Iqx0vpnEoVIete7kGem+UhBpQZjDaErKyrS1KzVWGzQijWGFo1nNQwdDksWaZklfU1oPdbVGZ3dVoCwNWrm3ouZlkORrX7WJB97WoIi+ybOh3pWh6vi4wKDFotv8QxBHAWtbbQ8EHa45X1i7bc9fF9v+RUSapd4SqVolwYwdKNEQBUWsHDYWUZrBm0Sbl4JFhI0JLgKKn3eByXr5PgKh01CS7CQAbKICXtW7cgcwrXRXAS+azaxL/6/FVpkpTfV4J8s+mVXShZ3IJVyTWWxSVApwTvumuFZI7yOild0zQp3S6Gw7DcfFIqCpZUlW6ws7NNEASV5IYqaAlWA1U5U9evCU4in00IlHWS6SSOSSaxtu8pVpZkLvJ+Ozt9bNspMjVKnEWwNOHn2bZDu90pgWE5SKTMFB6UZNlTU51yelL9d0IfEVtq+UfuvzykQaHdHSrOmygDhBPYaumhD4J1CSgu3T3BLMX1Qq69UqoQ1lf4b92SZzSKKqKtUVlit1pe2diSJEHWivD+JOjEcVyShaVRIc+Vw0gaU9KkuNbH6yJQyY0TkK1uEVKxeStco2KHx0xP99C2xWFZEsjvqy4eBVvXqfAQswoI+mSrfMHrJZhsZOkYCpfk/2vvXGIkvao7/jv3e1Z19cy4AaEJRjGWokisgsXCVhBCeQCyomTDAhQpDiQbssljEdlixTJRFJFIEQ9BEIoSnkGALEVWQlg7ASUh5uF4gAiMIDaJZnq6q6u/+upeFueee2+P8DBtG6pa+Y7Uqqrv+7rqPs89539ewzAkRzk7bWxCSsDZ2mHSny1aO/XKFC02mUCS4vb2FmlBWiC0MWpj2rZhzQM6e+mTGJuOJ8kSWrpdmMpoHs3GJMoKucZ0jWnaIjegeBhyaIap52ZRs5zvly9fSRvBVGIjy1OUcbeMxRgzuHnzMB0aZm21/q9jloeu0bqObZ0j+o1xHB8vk3ShEuWQJGzF4IZUkPX0dMWNG9fPAOOlYcbq3im2M6ac9iWGaXNUVRquYgxnGIZkCcuWOWVEXdcmA4MB6V3XxqpHQ5a8qzo5kB4eHp6Za2uD9z45E1vguElrhlvamO/tzVNkSFVpMHHfq+pt4217x9aK7dPywLCCE9mlwqf4RvWO9wkLOw+YvhuMCmHRzWmlZlb3iQmo+X15RtQ250ytwdYTfI2lpcgLJCeOWywWyex9cqrYlPlfhbhQlstlwsnqvscHBbc3a08rWmDBAM8QMliuYS51kvbKcBNLn+IguU6Mw5hSJm+Cp7VMD6DBxZVDgk5K7Wq6RvGSputpuh4PiXmCnpjjMGgppAB7CcidJ+Zf18pI7NUKuJqVzcbB1D4DicdxxA9j6rtLkp+nbTV+cSOe5UrHrhKonWYglaDVepxX48YwjvSLebKSbja5uoox3r7vz1QQygxoABQD29+/lNQdk5D14IjxnuOgRQ9qlwpympe1bTLbyMbMbbOZ2vXssz9QKc7VdI1KuOtCik3SuNSshxGhpq5ahtMVm3Fk1vf0kdlYRezVaoV4z2YYmLU9805Tt9hBWdc1w+nAuFaMaVwbI3JJkk4hXcWhdXKyTCriOI7Jf9CYrSVFNEk0QxI5xGwWY1cXC1Wju04xPhfH6tLiEsc3j7RWQHT/wesanffq5mNpshUHXEajxCoJHifHR2zWA5v1EBmOqtAXDqPywbPBExzcOLrObC9vtBAsW8AqTizZqrTRdCFdm6UyO0nsFLRJtlPGPKTn86xz7+9fAnRj3Dg6YjGbaxHHQMSUFCD30Yfm6MgXUkuZAmOVNp9KAEdU0XvZ1IwunnDO6YlrFkq7b6EQJsp77GTOuIAVqeg6dcmw37PNb8xsHcsxlWEuJjU1zaWEV9lpV8ZXWnbOMs2smcRNrA/R6XR9OuCqbDYvVa1hGNQIsBkRck03i1208TN1VT3DF6nAg81n6dluG0AZ7yqp+ykTLCSnyTJjqqkqJQajlscj9vcVPjDA2CS7EvA3ycc5x3qwnGO5nh7AjRvXNQg3/p9JD8M40vYabOy9pyFLuLZWbZ2am8p6nSsSl75rTeOSe4MxbqsmDtkbXnNT5ZhUWwfGsKz/FjBtbei6nk30th/HMYXimONv27ooTERsbOVTRMZmo5kdzN/QtJOy3Lura27ePLp4Vj9xwsnpUj3OozRQ4kiQAWpbQCU2YwMOucgmZFBWq3P46CF7VARd5u84OVkmAFt/L288E6HNec/EYAOtTY20U9DUSeCMYyaQYqhKJ1MTw41hWNDzcrmM8YR5M9vz9px5R1dVzf5+rpxr6sF6PSafHMPQ1MfIJ58mO/XtxDbVzkJEbDOYSl3ma8+bJ0u8CXerHXXb0rcqXZlaa/9j0oFVKimDcDOgn/G0EqMqzfJWIqzchMaULCmguU1YFozr168nE3vGkVTVts/miAuckQLVqjskhmbAuHMuhmtxptq395oqZXmy1HTVyecu446Q4zwN/7S11kWLoI3rfD6PeGbG+MxYZH3e29M1bwy09NWzNVu2PwsG2YWgbfV7nHMcHBwkdc72pIHq5oBq31UeeNbOMtOnqc4XTqICcDHd7uhVRTqrRrnihDBLzhC9aj3Bm2SQLVvlRBh2YvF0dqLawAEJgDT/n1EAs7y4nMqYiHnd6o2ba+45nCudBdWHa39/EU+TjK/ZSW2gs1kHLXe8Dky2EBpGZox2vR6oC3zFYhSBaEXTfNhmWTKV1FSFfJouuXz5Stp8thFnXZYIzNlQpbeI34mqtpsQg3wL68/JyRLaOtXGcxD947L0aRiM4StlXqbNZqQu4vgs+LX03TEma/UNsx9YToVs+IpKgtnMbs6YpV9euVbKzWuHmQHVIg6CKySvkfW4iphRzillG3o+nxMcKZdY07T4YcCq9pTOwcZ8zafKroHiP8MwJN8vk6gMaLe1aL9t7TYc0Zi1qdcm8elv9inyQJyjimXSNlFDyAkAsqd91ymGdeXgIMXWtq2uiYwV+9Q3w4CPT5ZJmLhj/nCup39CJAhN3TOfLWjqvggpyXF4Kl6rBWK1XDEOA+N6xWZccav367Ba0bcttdNUJn3b40fPrIuhM55kEnajx5+OqXoyo0+VYlfDipNhYO1HRpfBZsNE1KKv5l9zkDQVC3STuNohFQkPUpF+pG161kPW+4fVitVyyTAOjF4Z2iaM9HNlrOMw0Nc1DggbrbTTRzVqsx6QjWfRKI4VG4bHp0VljNByVukGVhV0NutZLo/YeDg8POLSYsEigt0GZDdNfcZ/q5v1hNVI51rC6NmMem8TtK9BHLOmZxZN+82sT3PSNTVNVePHgfXpknFY0VQxaaFTFw6HJuZraw3cxqt0oJ7zMVNADJ/qupYrBwfJCqvL2qF5nmqWyxXStcwvLWhioHpVkbyshZqm7VmPnoBDXE3TtikFb1XXeNRbfliPiKuTMcSHEVd5Nt6DqNTUzxSnurS/0GSFpytqHFWARhx+yGqb4XaQmatZZ20dmRe3q2rarmexv0hqmMEgJlEZ7qZ+TnoYVa4leJe+19RICxWzAwqiZXrjqSPuaSl7RHSddF2PNI5m1uIdXH6JHnC29u2w914999Vgoxjx8nTg/44ONX1y3XKe7Ak7IVEFKAbaU7kcimIuAYYttG0L3rNaZcuZxSkBZ6oGmzXPGFgpKpuE5iotgHAyrKgrBZ3NnaDrenw1so4OjXttn50ZfbbqmQXETg3DSexUKb21vY8pPkaNzvebjKGJmKdJzvNjp7NElauJoRiqdun3NwbCO0fjcoLAbKmsk7RlC9VUiuTf5T0BLbx6eHiIw9PPFwl7AJAqY39V5XBdlhytTH1pkjZvZ3UvGRVol5wtcrMhza+NlZnxgeI1xtA59Z3SoO85jpjvvdWyYKbW2fiX/mjL5ZJKHBVE/6uR4+Oswo4+h5IcHy8Tg1cVM+OeRKOLrbW60Xko0/ya6meSWZaQcu4uU2dL1xFTXc2KWdc54+Z6zNk9q8olx85SVTO4JAfBR187l0OaVGLvk0pfWkAtTs9HS52121RGG6vVqU/+iyaVJRU3eI6O1Po8m80TthiicSk4x3gyJMz3Tmkn0ryIyE3gyW234wXSS7lNpZ0LQlMfdoP+v/ThZ0MIL7uTL9sJiQp4MoTw2m034oWQiHxx6sP2aerDbtCL3YedwKgmmmiiiW5HE6OaaKKJdp52hVF9YNsNeBFo6sNu0NSH3aAXtQ87AaZPNNFEE92OdkWimmiiiSZ6TpoY1UQTTbTztHVGJSJvFpEnReSaiDy87fY8F4nIK0XkCyLyVRH5ioj8frx+ICL/KCJPxde74nURkb+M/fqyiNy33R4oiUglIv8mIo/Gz68SkcdjOz8uIm283sXP1+L9e7bZ7pJE5IqIfEpEvi4iXxORBy7gPPxhXEdPiMhHRaTf9bkQkb8WkWdE5Ini2rnHXUQeis8/JSIP3dGPhxC29gdUwDeAe4EW+A/g1dts023aehW4L77fB/4LeDXwp8DD8frDwJ/E9w8C/4BWBbofeHzbfYjt+iPg74BH4+dPAG+N798HvDO+/z3gffH9W4GPb7vtRR8+AvxufN8CVy7SPACvAL4FzIo5+O1dnwvg9cB9wBPFtXONO3AAfDO+3hXf3/Vjf3vLE/YA8Fjx+RHgkW0vpDts+2eBX0U96q/Ga1dR51WA9wNvK55Pz22xzXcDnwd+CXg0LqIfAPWt8wE8BjwQ39fxOdmBcb8cN7nccv0izcMrgO/EzVrHuXjTRZgL4J5bGNW5xh14G/D+4vqZ557rb9uqn02Y0dPx2k5TFL1fAzwOvDyE8L146/vAy+P7Xezbe4A/JocUvgS4HnIZm7KNqf3x/o34/LbpVcCzwIejCvtBEdnjAs1DCOG7wJ8B3wa+h47tl7h4cwHnH/fnNR/bZlQXjkRkAfw98AchhMPyXtAjYif9PUTk14BnQghf2nZbXiDVqPrx3hDCa4BjVOVItMvzABBxnN9Ame7PAHvAm7faqBeBfpLjvm1G9V3glcXnu+O1nSQRaVAm9bchhE/Hy/8jIlfj/avAM/H6rvXtF4FfF5H/Bj6Gqn9/AVwRq490to2p/fH+ZeB/f5oNfg56Gng6hPB4/PwplHFdlHkA+BXgWyGEZ0MIa+DT6PxctLmA84/785qPbTOqfwV+Llo7WhQo/NyW2/QjSTR5zoeAr4UQ/ry49TnALBcPodiVXf+taP24H7jgrIMGAAABIklEQVRRiMg/dQohPBJCuDuEcA86zv8cQvhN4AvAW+Jjt7bf+vWW+PzWpZQQwveB74jIz8dLvwx8lQsyD5G+DdwvIvO4rqwPF2ouIp133B8D3igid0XJ8o3x2u1pm6BiHOsHUQvaN4B3bbs9t2nn61Cx9svAv8e/B1Gs4PPAU8A/AQfxeQH+KvbrP4HXbrsPRV/eQLb63Qv8C3AN+CTQxet9/Hwt3r932+0u2v8LwBfjXHwGtR5dqHkA3g18HXgC+Bug2/W5AD6KYmprVLL9necz7sA7Yl+uAW+/k9+eQmgmmmiinadtq34TTTTRRD+WJkY10UQT7TxNjGqiiSbaeZoY1UQTTbTzNDGqiSaaaOdpYlQTTTTRztPEqCaaaKKdpx8C630Vd1fig/4AAAAASUVORK5CYII=\n", + "data:image/png;base64,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\n", + "data:image/png;base64,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\n", + "data:image/png;base64,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\n", + "data:image/png;base64,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\n", + "data:image/png;base64,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\n", + "data:image/png;base64,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\n", + "data:image/png;base64,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\n", + "data:image/png;base64,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\n", + "data:image/png;base64,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\n", + "http://legacy.python.org/dev/peps/pep-0465/", + "https://commons.wikimedia.org/wiki/File:Ball_python_lucy.JPG", + "https://creativecommons.org/licenses/by-sa/3.0/deed.en", + "https://docs.python.org/3/library/array.html", + "https://en.wikipedia.org/wiki/", + "https://en.wikipedia.org/wiki/Piano_key_frequencies", + "https://en.wikipedia.org/wiki/User:HCA", + "https://matplotlib.org/", + "https://matplotlib.org/contents.html", + "https://matplotlib.org/gallery/index.html", + "naucse:page?lesson=intro/notebook", + "naucse:static?filename=python.jpg", + "naucse:static?filename=sample.wav", + "naucse:static?filename=secret.png" + ], + "modules": { + "katex": "0.7.1" + }, + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/numpy/index.ipynb", + "title": "NumPy", + "vars": {} + } + }, + "source_file": "lessons/intro/numpy/info.yml", + "static_files": { + "python.jpg": { + "path": "numpy/python.jpg" + }, + "sample.wav": { + "path": "numpy/sample.wav" + }, + "secret.png": { + "path": "numpy/secret.png" + } + }, + "title": "NumPy" + }, + "intro/pandas": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Petr Viktorin, Miro Hrončok a další, 2016-2017." + ], + "content": { + "path": "pandas/index.html" + }, + "ids": [ + "analyza_dat_v_pythonu", + "grafy", + "groupby", + "indexer_codeiloccode", + "indexer_codeloccode", + "indexy", + "merge", + "nan_neboli_null_ci_na", + "pandas", + "presypani_dat", + "proces_analyzy_dat", + "sloupce", + "tabulky", + "tabulky_a_vybirani_prvku" + ], + "license": "cc-by-sa-40", + "links": [ + "#analyza_dat_v_pythonu", + "#grafy", + "#groupby", + "#indexer_codeiloccode", + "#indexer_codeloccode", + "#indexy", + "#merge", + "#nan_neboli_null_ci_na", + "#pandas", + "#presypani_dat", + "#proces_analyzy_dat", + "#sloupce", + "#tabulky", + "#tabulky_a_vybirani_prvku", + "data:image/png;base64,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\n", + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYAAAAE7CAYAAADUylYJAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAF05JREFUeJzt3Xu4XXV95/H3R8JVuUrKaACDmqooXiPgpY4FB8E6ovOI\nUqumDpW5oHhpx2pnplBbZuo8Lajt6JSKTuSxIqKOeBktBRFtBQ0XiYAMGa4R1DggIl4w+J0/1grs\nxJOcfcJhr33ye7+eZz9nrd/6rZXvzkn2Z6+1fmutVBWSpPY8ZOgCJEnDMAAkqVEGgCQ1ygCQpEYZ\nAJLUKANAkhplAEhSowwASWqUASBJjVo0dAFbsvfee9fSpUuHLkOSFpRLL730B1W1eLZ+Ux0AS5cu\nZdWqVUOXIUkLSpKbxunnISBJapQBIEmNMgAkqVEGgCQ1ygCQpEYZAJLUKANAkhplAEhSo6b6QjBp\nECfvPo/bunP+tiXNM/cAJKlRBoAkNcoAkKRGGQCS1CgDQJIaZQBIUqMMAElqlAEgSY0yACSpUQaA\nJDXKAJCkRhkAktQoA0CSGmUASFKjDABJapQBIEmNMgAkqVEGgCQ1ygCQpEYZAJLUqLECIMlbklyV\n5FtJPppkpyQHJLkkyXVJPpZkh77vjv38mn750pHtvKNvvzbJCx+ctyRJGsesAZBkCXAisLyqngRs\nBxwLvAs4raqWAXcAx/WrHAfcUVWPBU7r+5HkwH69JwJHAu9Lst38vh1J0rjGPQS0CNg5ySJgF+A2\n4DDgnH75SuCl/fTR/Tz98sOTpG8/q6p+XlU3AGuAgx/4W5AkbY1ZA6CqvgP8BXAz3Qf/ncClwA+r\nan3fbS2wpJ9eAtzSr7u+7//w0fYZ1rlPkuOTrEqyat26dVvzniRJYxjnENCedN/eDwAeCTwUOGqG\nrrVhlc0s21z7xg1Vp1fV8qpavnjx4tnKkyRtpXEOAb0AuKGq1lXVL4BPAs8G9ugPCQHsC9zaT68F\n9gPol+8O3D7aPsM6kqQJGycAbgYOTbJLfyz/cOBq4EvAy/s+K4BP99Pn9vP0yy+oqurbj+1HCR0A\nLAO+Pj9vQ5I0V4tm61BVlyQ5B7gMWA9cDpwOfA44K8mf9W1n9KucAZyZZA3dN/9j++1cleRsuvBY\nD5xQVffO8/uRJI1p1gAAqKqTgJM2ab6eGUbxVNXPgGM2s51TgFPmWKMk6UHglcCS1CgDQJIaZQBI\nUqMMAElqlAEgSY0yACSpUQaAJDXKAJCkRhkAktQoA0CSGmUASFKjDABJapQBIEmNMgAkqVEGgCQ1\nygCQpEYZAJLUKANAkhplAEhSowwASWqUASBJjVo0dAGSNK9O3n0et3Xn/G1rCrkHIEmNMgAkqVEG\ngCQ1ygCQpEYZAJLUKANAkhplAEhSowwASWqUASBJjTIAJKlRBoAkNcoAkKRGGQCS1CgDQJIaZQBI\nUqPGCoAkeyQ5J8m3k1yT5FlJ9kpyXpLr+p979n2T5L1J1iS5MsnTR7azou9/XZIVD9abkiTNbtw9\ngPcAX6iqxwNPAa4B3g6cX1XLgPP7eYCjgGX963jg/QBJ9gJOAg4BDgZO2hAakqTJmzUAkuwGPA84\nA6Cq7qmqHwJHAyv7biuBl/bTRwMfrs7FwB5JHgG8EDivqm6vqjuA84Aj5/XdSJLGNs4ewKOBdcCH\nklye5ANJHgrsU1W3AfQ/f63vvwS4ZWT9tX3b5to3kuT4JKuSrFq3bt2c35AkaTzjBMAi4OnA+6vq\nacDd3H+4ZyaZoa220L5xQ9XpVbW8qpYvXrx4jPIkSVtjnABYC6ytqkv6+XPoAuF7/aEd+p/fH+m/\n38j6+wK3bqFdkjSAWQOgqr4L3JLkcX3T4cDVwLnAhpE8K4BP99PnAq/tRwMdCtzZHyL6InBEkj37\nk79H9G2SpAEsGrPfG4GPJNkBuB54HV14nJ3kOOBm4Ji+7+eBFwFrgJ/0famq25P8KfCNvt87q+r2\neXkXkqQ5GysAquoKYPkMiw6foW8BJ2xmOx8EPjiXAiVJDw6vBJakRhkAktQoA0CSGmUASFKjDABJ\napQBIEmNMgAkqVHjXgimgRy08qB529bqFavnbVuSFj73ACSpUQaAJDXKAJCkRhkAktQoA0CSGmUA\nSFKjDABJapQBIEmNMgAkqVEGgCQ1ygCQpEYZAJLUKANAkhplAEhSowwASWqUASBJjTIAJKlRBoAk\nNcoAkKRGGQCS1CgDQJIaZQBIUqMMAElqlAEgSY0yACSpUQaAJDXKAJCkRhkAktSosQMgyXZJLk/y\n2X7+gCSXJLkuyceS7NC379jPr+mXLx3Zxjv69muTvHC+34wkaXxz2QN4E3DNyPy7gNOqahlwB3Bc\n334ccEdVPRY4re9HkgOBY4EnAkcC70uy3QMrX5K0tcYKgCT7Ar8FfKCfD3AYcE7fZSXw0n766H6e\nfvnhff+jgbOq6udVdQOwBjh4Pt6EJGnuxt0DeDfwNuCX/fzDgR9W1fp+fi2wpJ9eAtwC0C+/s+9/\nX/sM60iSJmzWAEjyYuD7VXXpaPMMXWuWZVtaZ/TPOz7JqiSr1q1bN1t5kqStNM4ewHOAlyS5ETiL\n7tDPu4E9kizq++wL3NpPrwX2A+iX7w7cPto+wzr3qarTq2p5VS1fvHjxnN+QJGk8swZAVb2jqvat\nqqV0J3EvqKrfAb4EvLzvtgL4dD99bj9Pv/yCqqq+/dh+lNABwDLg6/P2TiRJc7Jo9i6b9YfAWUn+\nDLgcOKNvPwM4M8kaum/+xwJU1VVJzgauBtYDJ1TVvQ/gz5ckPQBzCoCquhC4sJ++nhlG8VTVz4Bj\nNrP+KcApcy1SkjT/vBJYkhplAEhSowwASWqUASBJjTIAJKlRBoAkNcoAkKRGGQCS1CgDQJIaZQBI\nUqMMAElqlAEgSY0yACSpUQaAJDXKAJCkRhkAktQoA0CSGmUASFKjHsgzgaWpsvTtn5uX7dy407xs\nRpp67gFIUqMMAElqlAEgSY0yACSpUQaAJDXKAJCkRhkAktQoA0CSGmUASFKjDABJapQBIEmNMgAk\nqVEGgCQ1ygCQpEYZAJLUKANAkhplAEhSowwASWrUrAGQZL8kX0pyTZKrkrypb98ryXlJrut/7tm3\nJ8l7k6xJcmWSp49sa0Xf/7okKx68tyVJms04ewDrgd+vqicAhwInJDkQeDtwflUtA87v5wGOApb1\nr+OB90MXGMBJwCHAwcBJG0JDkjR5sz4UvqpuA27rp+9Kcg2wBDgaeH7fbSVwIfCHffuHq6qAi5Ps\nkeQRfd/zqup2gCTnAUcCH53H9/OAzNdDxQFu/PPfmrdtSdKDYdYAGJVkKfA04BJgnz4cqKrbkvxa\n320JcMvIamv7ts21b/pnHE+358D+++8/l/IkLVDz+uVrp3nb1DZv7JPASR4GfAJ4c1X9aEtdZ2ir\nLbRv3FB1elUtr6rlixcvHrc8SdIcjRUASban+/D/SFV9sm/+Xn9oh/7n9/v2tcB+I6vvC9y6hXZJ\n0gDGGQUU4Azgmqo6dWTRucCGkTwrgE+PtL+2Hw10KHBnf6joi8ARSfbsT/4e0bdJkgYwzjmA5wCv\nAVYnuaJv+yPgz4GzkxwH3Awc0y/7PPAiYA3wE+B1AFV1e5I/Bb7R93vnhhPCkqTJG2cU0FeZ+fg9\nwOEz9C/ghM1s64PAB+dSoCTpweGVwJLUKANAkhplAEhSowwASWqUASBJjTIAJKlRBoAkNWpON4OT\nNDcHrTxo3ra1esXqeduWBO4BSFKzDABJapQBIEmNMgAkqVEGgCQ1ygCQpEYZAJLUKANAkhplAEhS\nowwASWqUASBJjTIAJKlRBoAkNcoAkKRGGQCS1CgDQJIaZQBIUqMMAElqlAEgSY0yACSpUQaAJDVq\n0dAFbLNO3n1+tnPA/vOzHUnahHsAktQoA0CSGmUASFKjPAcgSZtx0MqD5mU7q1esnpftzDf3ACSp\nUQaAJDXKAJCkRk08AJIcmeTaJGuSvH3Sf74kqTPRAEiyHfDfgaOAA4HfTnLgJGuQJHUmvQdwMLCm\nqq6vqnuAs4CjJ1yDJInJDwNdAtwyMr8WOGS0Q5LjgeP72R8nuXZCtc2rjNdtb+AHW+7yrQdcywb5\n3TGratwc/pb8/U2h+fu/B/P1+xvgd/eocTpNOgBm+luojWaqTgdOn0w5w0qyqqqWD12Hto6/v4XL\n311n0oeA1gL7jczvC9w64RokSUw+AL4BLEtyQJIdgGOBcydcgySJCR8Cqqr1Sd4AfBHYDvhgVV01\nyRqmTBOHurZh/v4WLn93QKpq9l6SpG2OVwJLUqMMAElqlAEgSY0yACQ1JZ2HDl3HNDAApK2UZM8k\nTx66Ds0uyYeT7JZkF+Aq4IYkbx26rqE5CmjCknyGTa5+Bu4EVgF/U1U/m3xVGleSC4GX0A2hvgJY\nB3y5qpr/MJlmSS6vqqcleRXdPcneBqyqqqYD3D2Aybse+DHwt/3rR8D3gF/v5zXddq+qHwH/CvhQ\nVT0DeMHANWl2OyRZRHfzyf/V34zylwPXNDifCTx5T6uq543MfybJRVX1vCQtXxS3UCxK8gjgFcB/\nHLoYje0DwM10d3f7cpL96b6INc09gMlb3P/jA6Cf3rufvWeYkjQH76S7kn1NVX0jyaOB6wauSbOo\nqtOq6pFVdUR1x71vAQ4buq6heQ5gwpK8CPgfwP+luzvqAcC/By4EXl9V7x6uOmnbkuTELS2vqvdO\nqpZp5CGgCauqzydZBjyeLgC+PXLi1w//KZdkJfCmqvphP78n8JdV9a+HrUybsXjoAqaZewADSPJs\nYCkjAVxVHx6sII1tw2iS2dqkhcA9gAlLcibwGLohhPf2zQUYAAvDQ5LsWVV3ACTZC/8fTb0kjwTe\nAzy3b7oIeEtVNf08Ev/hTt5y4MBy12uh+kvgn5Kc088fA5wyYD0az4eAc4BX9/Ov6dteOFhFU8BD\nQBOW5OPAiVV129C1aOskOZBuBEmA86vq6oFL0iySXFFVT52trTXuAUze3sDVSb4O/HxDY1W9ZLiS\nNJsku1XVj/pDPt8F/m5k2V5Vdftw1WkMtyc5FvhYP/8KoPnfmXsAE5bkn8/UXlVfnnQtGl+Sz1bV\ni5PcwMa38ghQVfXogUrTGJIsBd4HHNI3fQ14Y1XdMFRN08AAkKRGeQhoQpJ8taqem+QuZv4GudtA\npWmOkiwBHsXGw3gvGq4izcZRQDNzD0CagyTvAl4JXM3IMF7P4Uy3JF+kGwW0Ybj1a4BjqspRQJqs\nJNsB+7DxN8ibh6tI40pyLfDkqvr5rJ01NRwFNDMPAU1YkjcCJ9HdAnrD7WgLaPq+5AvI9cD2jIzg\n0oLgKKAZuAcwYUnWAIdU1f8buhbNXZJPAE8BzmfjYbxbvOmYhrXJKKACLsZRQO4BDOAWuieAaWE6\nt39pAUjyhqr666q6EXjR0PVMG/cAJmTk+aNPBB4HfI6Nv0GeOkRdmrskO9A9wQ3g2qr6xZD1aPOS\nXFZVTx+6jmnlHsDk7Nr/vLl/7dC/4FefEawpleT5wErgRrohvPslWeEwUC1E7gFMWJJjqurjs7Vp\nOiW5FHhVVV3bz/868NH+2cCaMknWAz+ZaRFef+MjIQfwjjHbNJ223/DhD1BV/4duVJCm0+qq2m2G\n166tf/iDh4AmJslRdCehliQZfQzdbsD6YarSVliV5AzgzH7+d4BLB6xH2moGwOTcCqwCXsLGHxh3\nAW8ZpCJtjX8HnACcSHcY4SK64YWaTh5a3QLPAUxYku3pPjgcRbLA9Fdwr6yqV8/aWVoA3AOYvGfT\n3Y/kRhxFsqBU1b1JFifZoaruGboe6YEyACbvVOCITUeRAI4iWRhuBP4xybnA3RsavY5jeiV5CPDy\nqjp76FqmjaOAJs9RJAvbrcBn6f7v7Nq/HjZoRdqiqvol8Iah65hG7gFMnqNIFrarZ7qOY6hiNLbz\nkvwB3c3gRvfcmr4hnCeBJyzJjnSjSJ7LyCgSby+8MMx0awFvNzD9+kd5bqr5R3kaANIYRq7jeAX3\n31IYuus4DqyqgwcpTHoAPAQ0IUlWs4V7/lSVzwOYbl7HsYAl2QV4K7B/VR2fZBnwuKr67MClDco9\ngAnp/8HtQ3c76FGPAm6tqjWTr0pzlWQ34O6quref3w7Ysapmut+MpkSSj9EF92ur6klJdga+1voT\nwRwFNDmnAT+qqptGX3Q3qjpt4No0vr8Hdh6Z3xn4h4Fq0fgeU1X/DfgFQFX9lO4cXNMMgMlZWlVX\nbtpYVauApZMvR1tpp6r68YaZfnqXAevReO7pv/UXQJLH4GM9DYAJ2mkLy3bewjJNl7uT3DfiJ8kz\ngJ8OWI/GczLwBbor7z9C90jPtw1a0RTwHMCEJPkocEFV/e0m7cfRXRn8ymEq01wkeSZwFt1JYYBH\nAK+sKq/lmHJJHg4cSnfo5+Kq+sHAJQ3OAJiQJPsAnwLu4f5RJMvpngr2sqr67lC1aW76G/o9ju6D\n5NvezG/6JTmT7pqbr1TVt4euZ1oYABOW5DeBJ/WzV1XVBUPWo7kZGU74qKp6vcMJF4Ykh9FdfPkb\nwKOBK4CLquo9gxY2MANAmgOHEy5c/ZDdZwK/Cfxb4KdV9fhhqxqWF4JJc/OYqnplkt+GbjhhkuaH\nE067JOcDDwW+BnwFeGZVfX/YqobnKCBpbhxOuDBdSXf+7UnAk4ENe29N8xCQNAdJ/gXwn4AD6S4K\new7wu1V14ZB1aTxJHga8DvgD4J9V1Y4DlzQoA0CaI4cTLjxJ3kB3AvgZwE3cPyKo6UEYBoA0htGL\nv2ZSVZdNqhbNXZL/QPehf2lVrR+6nmlhAEhjSPKlLSyuqjpsYsVoqyR5Ct1eAHTf/r85ZD3TwACQ\ntM1LciJwPPDJvullwOlV9VfDVTU8RwFJY0jytpHpYzZZ9l8mX5Hm6PeAQ6rqj6vqj+nO4bx+4JoG\nZwBI4zl2ZPodmyw7cpKFaKsEuHdk/l68HbQXgkljymamZ5rX9PkQcEmST/XzLwXOGLCeqWAASOOp\nzUzPNK8pU1WnJrmQ7n5AAV5XVZcPW9XwPAksjSHJvcDddB8eO9M9yY1+fqeq2n6o2rR5SXaiu+/P\nY4HVwBkOA72fASBpm9XfvO8XdPf/OQq4sarePGxV08MAkLTNSrK6qg7qpxcBX6+qLV7U1xJHAUna\nlt33sB4P/fwq9wAkbbNGzt3AxudvQncF925D1TYNDABJapSHgCSpUQaAJDXKAJCkRhkA0ogkz0/y\n7KHrkCbBAJA29nzgQQ2AdPy/p8H5j1BNSPLaJFcm+WaSM5P8yySXJLk8yT8k2SfJUrrbBrwlyRVJ\nfiPJ4iSfSPKN/vWcfnuLk5yX5LIkf5PkpiR798vemuRb/evNfdvSJNckeR9wGfCfk5w2Ut/rk5w6\n6b8Xtc1hoNrmJXki3YNAnlNVP0iyF90N3H5YVZXk94AnVNXvJzkZ+HFV/UW/7t8B76uqrybZH/hi\nVT0hyV8D36mq/5rkSOB/A4uBRwH/k/ufGXwJ8GrgDuB64NlVdXGShwJXAo+vql8k+Sfg31TV6gn9\ntUjeDVRNOAw4Z8PD26vq9iQHAR9L8ghgB+CGzaz7AuDA5L47Pu+WZFe6u0q+rN/eF5Lc0S9/LvCp\nqrobIMkn6R5DeC5wU1Vd3K9zd5ILgBcnuQbY3g9/TZoBoBaEX71l818Bp1bVuUmeD5y8mXUfAjyr\nqn660QZHEmGGP2tz7t5k/gPAHwHfprtfvTRRngNQC84HXpHk4QD9IaDdge/0y1eM9L0L2HVk/u+B\nN2yYSfLUfvKrwCv6tiOAPfv2i4CXJtmlP8zzMro7Uf6KqroE2A94FfDRrX1z0tYyALTNq6qrgFOA\nLyf5JnAq3Tf+jyf5CvCDke6fAV624SQwcCKwvD+BfDXdSWKAPwGOSHIZ3W2GbwPuqqrL6M4BfJ3u\n+P8HZnnwyNnAP1bVHVvoIz0oPAksbYUkOwL3VtX6JM8C3l9VT51tvRm281ngtKo6f96LlGbhOQBp\n6+wPnN2P578HeP1cVk6yB91ewjf98NdQ3AOQpEZ5DkCSGmUASFKjDABJapQBIEmNMgAkqVH/H5Hp\n1WkeGrroAAAAAElFTkSuQmCC\n", + "data:image/png;base64,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\n", + "data:image/png;base64,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\nYwLm/wFs+GhAV4F94AAAAABJRU5ErkJggg==\n", + "http://pandas.pydata.org/pandas-docs/stable/comparison_with_sql.html", + "http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.fillna.html", + "http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.merge.html", + "http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.set_index.html", + "http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.stack.html", + "http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.unstack.html", + "http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html", + "http://pandas.pydata.org/pandas-docs/stable/reshaping.html", + "http://pandas.pydata.org/pandas-docs/version/0.18.0/generated/pandas.MultiIndex.droplevel.html", + "http://pandas.pydata.org/pandas-docs/version/0.19.0/visualization.html", + "naucse:page?lesson=intro/notebook", + "naucse:page?lesson=intro/numpy", + "naucse:static?filename=actors.csv", + "naucse:static?filename=spouses.csv" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/pandas/index.ipynb", + "title": "Pandas", + "vars": {} + } + }, + "source_file": "lessons/intro/pandas/info.yml", + "static_files": { + "actors.csv": { + "path": "pandas/actors.csv" + }, + "spouses.csv": { + "path": "pandas/spouses.csv" + }, + "style-table.css": { + "path": "pandas/style-table.css" + } + }, + "title": "Pandas" + }, + "intro/pyglet": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2015-2017.", + "Ikonku hada vytvořil Martin Berube." + ], + "content": { + "path": "pyglet/index.html" + }, + "ids": [ + "animace", + "cas_", + "grafika", + "interaktivni_programy", + "klik_", + "pokracovani_priste", + "pyglet_", + "text", + "vykreslovani_", + "zavolej_pozdeji" + ], + "license": "cc-by-sa-40", + "links": [ + "#animace", + "#cas_", + "#grafika", + "#interaktivni_programy", + "#klik_", + "#pokracovani_priste", + "#pyglet_", + "#text", + "#vykreslovani_", + "#zavolej_pozdeji", + "http://pyglet.readthedocs.org/en/latest/index.html", + "https://cs.wikipedia.org/wiki/Sprite_%28po%C4%8D%C3%ADta%C4%8Dov%C3%A1_grafika%29", + "https://pyvec.github.io/cheatsheets/pyglet/pyglet-basics-cs.pdf", + "https://www.google.cz/search?tbs=ift:png&tbm=isch&q=snake+icon", + "naucse:page?lesson=beginners/functions", + "naucse:page?lesson=projects/pong", + "naucse:static?filename=had.png", + "naucse:static?filename=had2.png", + "naucse:static?filename=pong.py" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/pyglet/index.md", + "title": "Grafika", + "vars": {} + } + }, + "source_file": "lessons/intro/pyglet/info.yml", + "static_files": { + "had.gif": { + "path": "pyglet/had.gif" + }, + "had.png": { + "path": "pyglet/had.png" + }, + "had2.png": { + "path": "pyglet/had2.png" + }, + "pong.py": { + "path": "pyglet/pong.py" + } + }, + "title": "Grafika" + }, + "intro/pyqt": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Petr Viktorin, Miro Hrončok a další, 2016-2017." + ], + "content": { + "path": "pyqt/index.html" + }, + "ids": [ + "atributy", + "gui_v_pythonu_pyqt5", + "instalace", + "jmena_a_dokumentace", + "klikani_do_gridu", + "menu_a_modalni_dialog", + "modelview", + "moduly_qt", + "numpy", + "o_qt_pyqt_a_pyside", + "obrazky", + "prvni_aplikace", + "pyqt5", + "qlistwidget_-_paleta", + "qt5_designer", + "qt_designer", + "skladani_gui", + "smycka_udalosti_signaly_a_sloty", + "specifika_pyqt", + "sprava_pameti", + "tazeni_mysi", + "trida_pro_gui_aplikace", + "vice_tlacitek_mysi", + "vlastni_widget_-_grid" + ], + "license": "cc-by-sa-40", + "links": [ + "#atributy", + "#gui_v_pythonu_pyqt5", + "#instalace", + "#jmena_a_dokumentace", + "#klikani_do_gridu", + "#menu_a_modalni_dialog", + "#modelview", + "#moduly_qt", + "#numpy", + "#o_qt_pyqt_a_pyside", + "#obrazky", + "#prvni_aplikace", + "#pyqt5", + "#qlistwidget_-_paleta", + "#qt5_designer", + "#qt_designer", + "#skladani_gui", + "#smycka_udalosti_signaly_a_sloty", + "#specifika_pyqt", + "#sprava_pameti", + "#tazeni_mysi", + "#trida_pro_gui_aplikace", + "#vice_tlacitek_mysi", + "#vlastni_widget_-_grid", + "http://doc.qt.io/qt-5/layout.html#horizontal-vertical-grid-and-form-layouts", + "http://doc.qt.io/qt-5/model-view-programming.html", + "http://doc.qt.io/qt-5/qcombobox.html#activated", + "http://doc.qt.io/qt-5/qfiledialog.html#getOpenFileName", + "http://doc.qt.io/qt-5/qfiledialog.html#getSaveFileName", + "http://doc.qt.io/qt-5/qmessagebox.html#about", + "http://doc.qt.io/qt-5/qmessagebox.html#critical", + "http://doc.qt.io/qt-5/qobject.html", + "http://doc.qt.io/qt-5/qpainter.html", + "http://doc.qt.io/qt-5/qrect.html", + "http://doc.qt.io/qt-5/qt.html#ItemDataRole-enum", + "http://doc.qt.io/qt-5/qtcore-index.html", + "http://doc.qt.io/qt-5/qtgui-index.html", + "http://doc.qt.io/qt-5/qtmodules.html", + "http://doc.qt.io/qt-5/qtmultimedia-index.html", + "http://doc.qt.io/qt-5/qtnetwork-index.html", + "http://doc.qt.io/qt-5/qtsql-index.html", + "http://doc.qt.io/qt-5/qtsvg-index.html", + "http://doc.qt.io/qt-5/qtwidgets-index.html", + "http://doc.qt.io/qt-5/qtxml-index.html", + "http://doc.qt.io/qt-5/qwidget.html", + "http://doc.qt.io/qt-5/qwidget.html#keyPressEvent", + "http://doc.qt.io/qt-5/qwidget.html#mousePressEvent", + "http://doc.qt.io/qt-5/qwidget.html#paintEvent", + "http://kenney.nl/", + "http://opengameart.org/users/kenney", + "http://pyqt.sourceforge.net/Docs/PyQt5/designer.html#the-uic-module", + "http://pyqt.sourceforge.net/Docs/PyQt5/index.html", + "http://pyqt.sourceforge.net/Docs/PyQt5/installation.html#building-and-installing-from-source", + "https://doc.qt.io/qt-5/gallery.html", + "https://github.com/cvut/MI-PYT/issues/57", + "https://github.com/pyvec/naucse.python.cz/tree/master/lessons/intro/pyqt/static/pics", + "https://pypi.org/project/PyQt5/", + "https://pypi.org/project/bresenham/", + "https://pypi.python.org/pypi/pyqt5-tools", + "https://riverbankcomputing.com/software/pyqt", + "https://wiki.qt.io/PySide", + "https://www.gnu.org/licenses/gpl-3.0.en.html", + "https://www.google.cz/search?q=game+boy+overworld+map&tbm=isch", + "https://www.qt.io/", + "https://www.qt.io/download-open-source/#section-2", + "https://www.riverbankcomputing.com/software/pyqt/download5", + "https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtcore/qobject.html", + "https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qabstractbutton.html", + "naucse:page?lesson=intro/cython", + "naucse:page?lesson=intro/numpy", + "naucse:static?filename=basic-screenshot.png", + "naucse:static?filename=pics/grass.svg", + "naucse:static?filename=pics/wall.svg" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/pyqt/index.md", + "title": "GUI v Pythonu", + "vars": {} + } + }, + "source_file": "lessons/intro/pyqt/info.yml", + "static_files": { + "basic-screenshot.png": { + "path": "pyqt/basic-screenshot.png" + }, + "pics/README/index.md": { + "path": "pyqt/pics/README/index.md" + }, + "pics/arrows/down.svg": { + "path": "pyqt/pics/arrows/down.svg" + }, + "pics/arrows/left.svg": { + "path": "pyqt/pics/arrows/left.svg" + }, + "pics/arrows/right.svg": { + "path": "pyqt/pics/arrows/right.svg" + }, + "pics/arrows/up.svg": { + "path": "pyqt/pics/arrows/up.svg" + }, + "pics/castle.svg": { + "path": "pyqt/pics/castle.svg" + }, + "pics/dude1.svg": { + "path": "pyqt/pics/dude1.svg" + }, + "pics/dude2.svg": { + "path": "pyqt/pics/dude2.svg" + }, + "pics/dude3.svg": { + "path": "pyqt/pics/dude3.svg" + }, + "pics/dude4.svg": { + "path": "pyqt/pics/dude4.svg" + }, + "pics/dude5.svg": { + "path": "pyqt/pics/dude5.svg" + }, + "pics/fish.svg": { + "path": "pyqt/pics/fish.svg" + }, + "pics/fish2.svg": { + "path": "pyqt/pics/fish2.svg" + }, + "pics/grass.svg": { + "path": "pyqt/pics/grass.svg" + }, + "pics/lines/1.svg": { + "path": "pyqt/pics/lines/1.svg" + }, + "pics/lines/10.svg": { + "path": "pyqt/pics/lines/10.svg" + }, + "pics/lines/11.svg": { + "path": "pyqt/pics/lines/11.svg" + }, + "pics/lines/12.svg": { + "path": "pyqt/pics/lines/12.svg" + }, + "pics/lines/13.svg": { + "path": "pyqt/pics/lines/13.svg" + }, + "pics/lines/14.svg": { + "path": "pyqt/pics/lines/14.svg" + }, + "pics/lines/15.svg": { + "path": "pyqt/pics/lines/15.svg" + }, + "pics/lines/2.svg": { + "path": "pyqt/pics/lines/2.svg" + }, + "pics/lines/3.svg": { + "path": "pyqt/pics/lines/3.svg" + }, + "pics/lines/4.svg": { + "path": "pyqt/pics/lines/4.svg" + }, + "pics/lines/5.svg": { + "path": "pyqt/pics/lines/5.svg" + }, + "pics/lines/6.svg": { + "path": "pyqt/pics/lines/6.svg" + }, + "pics/lines/7.svg": { + "path": "pyqt/pics/lines/7.svg" + }, + "pics/lines/8.svg": { + "path": "pyqt/pics/lines/8.svg" + }, + "pics/lines/9.svg": { + "path": "pyqt/pics/lines/9.svg" + }, + "pics/shark.svg": { + "path": "pyqt/pics/shark.svg" + }, + "pics/wall.svg": { + "path": "pyqt/pics/wall.svg" + }, + "pics/wall2.svg": { + "path": "pyqt/pics/wall2.svg" + }, + "pics/water.svg": { + "path": "pyqt/pics/water.svg" + } + }, + "title": "GUI v Pythonu" + }, + "intro/requests": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017." + ], + "content": { + "path": "requests/index.html" + }, + "ids": [ + "chrante_sve_tokeny", + "github_api", + "pouziti_session", + "requests" + ], + "license": "cc-by-sa-40", + "license_code": "cc0", + "links": [ + "#chrante_sve_tokeny", + "#github_api", + "#pouziti_session", + "#requests", + "http://docs.python-requests.org/en/master/user/advanced/#session-objects", + "http://docs.python-requests.org/en/master/user/quickstart/", + "http://httpbin.org", + "https://cs.wikipedia.org/wiki/Basic_access_authentication", + "https://developer.github.com/v3", + "https://developer.github.com/v3/", + "https://docs.python.org/3/library/configparser.html", + "https://docs.python.org/3/library/urllib.request.html#module-urllib.request", + "https://github.com/settings/tokens", + "https://requests.readthedocs.io/en/master/", + "naucse:page?lesson=fast-track/http" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/requests/index.md", + "title": "Requests – Weboví klienti", + "vars": {} + } + }, + "source_file": "lessons/intro/requests/info.yml", + "static_files": {}, + "title": "Requests – Weboví klienti" + }, + "intro/testing": { + "pages": { + "index": { + "attribution": [ + "Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017." + ], + "content": { + "path": "testing/index.1.html" + }, + "ids": [ + "castecne_upravene_objekty_tridy_moduly_stubs", + "citlive_udaje", + "falesne_objekty_fakes", + "fixtures", + "integrace_s_pytestem", + "kam_dat_testy", + "komprimovane_citlive_udaje", + "ktere_http_pozadavky_jsou_stejne", + "kviz", + "ocekavani_mocks_spies", + "parametricke_testy", + "podvadeni", + "pytest", + "spousteni_testu_pomoci_codesetuppy_testcode", + "testovani", + "testovani_aplikaci_v_clicku", + "testovani_aplikaci_ve_flasku", + "testovani_http_komunikace_betamax", + "travis_ci", + "varovani" + ], + "license": "cc-by-sa-40", + "links": [ + "#castecne_upravene_objekty_tridy_moduly_stubs", + "#citlive_udaje", + "#falesne_objekty_fakes", + "#fixtures", + "#integrace_s_pytestem", + "#kam_dat_testy", + "#komprimovane_citlive_udaje", + "#ktere_http_pozadavky_jsou_stejne", + "#kviz", + "#ocekavani_mocks_spies", + "#parametricke_testy", + "#podvadeni", + "#pytest", + "#spousteni_testu_pomoci_codesetuppy_testcode", + "#testovani", + "#testovani_aplikaci_v_clicku", + "#testovani_aplikaci_ve_flasku", + "#testovani_http_komunikace_betamax", + "#travis_ci", + "#varovani", + "http://betamax.readthedocs.io/en/latest/implementation_details.html#gzip-content-encoding", + "http://betamax.readthedocs.io/en/latest/matchers.html", + "http://blog.horejsek.com/matka-moudrosti-jak-testovat", + "http://click.pocoo.org/6/testing/", + "http://doc.pytest.org/en/latest/example/index.html", + "http://doc.pytest.org/en/latest/fixture.html", + "http://doc.pytest.org/en/latest/goodpractices.html#choosing-a-test-layout-import-rules", + "http://doc.pytest.org/en/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner", + "http://doc.pytest.org/en/latest/parametrize.html", + "http://doc.pytest.org/en/latest/plugins.html", + "http://doc.pytest.org/en/latest/writing_plugins.html#conftest-py-local-per-directory-plugins", + "http://docs.pytest.org/en/latest/getting-started.html", + "http://flask.pocoo.org/docs/1.0/api/#flask.Response", + "http://flask.pocoo.org/docs/1.0/testing/", + "http://flexmock.readthedocs.io/en/latest/start/#creating-and-checking-expectations", + "http://pytest.org/", + "https://betamax.readthedocs.io/", + "https://betamax.readthedocs.io/en/latest/configuring.html#filtering-sensitive-data", + "https://cz.pycon.org/", + "https://docs.pytest.org/en/latest/monkeypatch.html", + "https://docs.travis-ci.com/user/languages/python/", + "https://en.wikipedia.org/wiki/HTTP_compression#Content-Encoding_tokens", + "https://flexmock.readthedocs.io/", + "https://github.com/PyConPL/Book/blob/master/2017/workshops/pytest_parametric_tests/text.md", + "https://travis-ci.com/", + "https://travis-ci.com/profile", + "https://travis-ci.org/", + "https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", + "https://www.youtube.com/watch?v=-nJ-ZW_LP7s", + "https://www.youtube.com/watch?v=iFqF5IaWfy0", + "naucse:page?lesson=beginners/testing", + "naucse:page?lesson=intro/click", + "naucse:page?lesson=intro/distribution", + "naucse:page?lesson=intro/requests" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/intro/testing/index.md", + "title": "Testování", + "vars": {} + } + }, + "source_file": "lessons/intro/testing/info.yml", + "static_files": {}, + "title": "Testování" + }, + "intro/turtle": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2014-2017." + ], + "content": { + "path": "turtle/index.html" + }, + "ids": [ + "_", + "a_kde_je_ta_zelva", + "ctverec", + "ctverec_ii", + "dlouha_prerusovana_cara", + "jak_opakovat_a_neopakovat_emseem", + "obdelnik", + "otaceni", + "prerusovana_cara", + "range", + "solution-0", + "solution-1", + "solution-2", + "solution-3", + "solution-4", + "solution-5", + "solution-6", + "solution-7", + "tri_ctverce", + "ukol_navic", + "vycet", + "zelvi_program" + ], + "license": "cc-by-sa-40", + "links": [ + "#_", + "#a_kde_je_ta_zelva", + "#ctverec", + "#ctverec_ii", + "#dlouha_prerusovana_cara", + "#jak_opakovat_a_neopakovat_emseem", + "#obdelnik", + "#otaceni", + "#prerusovana_cara", + "#range", + "#tri_ctverce", + "#ukol_navic", + "#vycet", + "#zelvi_program", + "naucse:solution?solution=0", + "naucse:solution?solution=1", + "naucse:solution?solution=2", + "naucse:solution?solution=3", + "naucse:solution?solution=4", + "naucse:solution?solution=5", + "naucse:solution?solution=6", + "naucse:solution?solution=7", + "naucse:static?filename=turtle-dashed.png", + "naucse:static?filename=turtle-dashed2.png", + "naucse:static?filename=turtle-hexagons.png", + "naucse:static?filename=turtle-rect.png", + "naucse:static?filename=turtle-square.png", + "naucse:static?filename=turtle-squares.png", + "naucse:static?filename=turtle-stairs.png" + ], + "slug": "index", + "solutions": [ + { + "content": "
from turtle import forward, left, exitonclick\n\nforward(50)\nleft(90)\nforward(50)\nleft(90)\nforward(50)\nleft(90)\nforward(50)\nleft(90)\n\nexitonclick()\n
" + }, + { + "content": "
from turtle import forward, left, exitonclick\n\nforward(100)\nleft(90)\nforward(50)\nleft(90)\nforward(100)\nleft(90)\nforward(50)\nleft(90)\n\nexitonclick()\n
" + }, + { + "content": "
from turtle import forward, left, exitonclick\n\nforward(50)\nleft(90)\nforward(50)\nleft(90)\nforward(50)\nleft(90)\nforward(50)\nleft(90)\n\nleft(20)\n\nforward(50)\nleft(90)\nforward(50)\nleft(90)\nforward(50)\nleft(90)\nforward(50)\nleft(90)\n\nleft(20)\n\nforward(50)\nleft(90)\nforward(50)\nleft(90)\nforward(50)\nleft(90)\nforward(50)\nleft(90)\n\nexitonclick()\n
" + }, + { + "content": "

Vypíšou se čísla od 0 do 4!\nProgram funguje steně, jako kdybys napsal/a:

\n
i = 0\nprint(i)\n\ni = 1\nprint(i)\n\ni = 2\nprint(i)\n\ni = 3\nprint(i)\n\ni = 4\nprint(i)\n

V sekvenci range(5) jsou čísla 0, 1, 2, 3 a 4. Je jich celkem pět.

" + }, + { + "content": "
from turtle import forward, penup, pendown, exitonclick\n\nfor i in range(10):\n    forward(10)\n    penup()\n    forward(5)\n    pendown()\n\nexitonclick()\n
" + }, + { + "content": "
from turtle import forward, penup, pendown, left, exitonclick\n\nfor i in range(20):\n    forward(i)\n    penup()\n    forward(5)\n    pendown()\n\nexitonclick()\n
" + }, + { + "content": "
from turtle import forward, left, exitonclick\n\nfor i in range(4):\n    forward(50)\n    left(90)\n\nexitonclick()\n
" + }, + { + "content": "
from turtle import forward, left, right, speed, exitonclick\n\n# Třikrát:\nfor i in range(3):\n\n    # Nakresli čtverec (kód zkopírovaný z předchozí úlohy a odsazený)\n    for j in range(4):\n        forward(50)\n        left(90)\n\n    # Otoč se o 20°\n    left(20)\n\nexitonclick()\n
" + } + ], + "source_file": "lessons/intro/turtle/index.md", + "title": "Želva a cykly", + "vars": {} + } + }, + "source_file": "lessons/intro/turtle/info.yml", + "static_files": { + "turtle-dashed.png": { + "path": "turtle/turtle-dashed.png" + }, + "turtle-dashed2.png": { + "path": "turtle/turtle-dashed2.png" + }, + "turtle-hexagons.png": { + "path": "turtle/turtle-hexagons.png" + }, + "turtle-rect.png": { + "path": "turtle/turtle-rect.png" + }, + "turtle-square.png": { + "path": "turtle/turtle-square.png" + }, + "turtle-squares.png": { + "path": "turtle/turtle-squares.png" + }, + "turtle-stairs.png": { + "path": "turtle/turtle-stairs.png" + } + }, + "title": "Želva a cykly" + }, + "meta/installing-naucse": { + "pages": { + "index": { + "attribution": [ + "Napsal Mikuláš Poul, 2018" + ], + "content": { + "path": "installing-naucse/index.html" + }, + "ids": [], + "license": "cc-by-sa-40", + "license_code": "cc0", + "links": [ + "naucse:page?lesson=meta/local-run" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/meta/installing-naucse/index.md", + "title": "Lokální instalace Nauč se Python", + "vars": {} + } + }, + "source_file": "lessons/meta/installing-naucse/info.yml", + "static_files": {}, + "title": "Lokální instalace Nauč se Python" + }, + "meta/local-run": { + "pages": { + "index": { + "attribution": [ + "Napsal Mikuláš Poul, 2018", + "Upravil Petr Viktorin, 2022" + ], + "content": { + "path": "local-run/index.html" + }, + "ids": [ + "codecourseymlcode_codecoursescode_coderunscode_definice_kurzu", + "codegithubworkflowsmainymlcode_instrukce_pro_github_actions", + "codelessonscode_texty_lekci", + "codelicensescode", + "codepyprojecttomlcode_codepoetrylockcode_codegithubcode_definice_projektu_a_zavislosti", + "definice_kurzu", + "definice_lekce", + "kontrola_nastaveni_projektu", + "lokalni_spusteni", + "misto_a_cas", + "naklonovani_zakladu", + "otestovani_kurzu", + "plan_lekci", + "povinne_informace", + "priprava", + "promenne", + "struktura_repozitare", + "upravovani_a_vytvareni_materialu", + "vytvoreni_lokalniho_kurzu" + ], + "license": "cc-by-sa-40", + "license_code": "cc0", + "links": [ + "#codecourseymlcode_codecoursescode_coderunscode_definice_kurzu", + "#codegithubworkflowsmainymlcode_instrukce_pro_github_actions", + "#codelessonscode_texty_lekci", + "#codelicensescode", + "#codepyprojecttomlcode_codepoetrylockcode_codegithubcode_definice_projektu_a_zavislosti", + "#definice_kurzu", + "#definice_lekce", + "#kontrola_nastaveni_projektu", + "#lokalni_spusteni", + "#misto_a_cas", + "#naklonovani_zakladu", + "#otestovani_kurzu", + "#plan_lekci", + "#povinne_informace", + "#priprava", + "#promenne", + "#struktura_repozitare", + "#upravovani_a_vytvareni_materialu", + "#vytvoreni_lokalniho_kurzu", + "https://cs.wikipedia.org/wiki/Markdown", + "https://github.com/pyvec/naucse-python", + "https://github.com/pyvec/naucse-python/blob/main/pyproject.toml", + "https://github.com/pyvec/naucse.python.cz/issues", + "https://python-poetry.org/docs/#installation", + "naucse:page?lesson=beginners/cmdline", + "naucse:page?lesson=beginners/install", + "naucse:page?lesson=beginners/venv-setup", + "naucse:page?lesson=fast-track/yaml", + "naucse:page?lesson=git/git-collaboration-2in1", + "naucse:page?lesson=git/install", + "naucse:page?lesson=intro/notebook", + "naucse:static?filename=info.yml" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/meta/local-run/index.md", + "title": "Vytvoření lokálního kurzu", + "vars": {} + } + }, + "source_file": "lessons/meta/local-run/info.yml", + "static_files": { + "info.yml": { + "path": "local-run/info.yml" + } + }, + "title": "Vytvoření lokálního kurzu" + }, + "meta/submitting-a-run": { + "pages": { + "index": { + "attribution": [ + "Napsal Mikuláš Poul, 2018" + ], + "content": { + "path": "submitting-a-run/index.html" + }, + "ids": [ + "definice_github_actions", + "informace_o_tvem_repozitari", + "nahrani_do_vlastniho_forku", + "pridani_kurzu_na_nauc_se_python", + "pull_request", + "upravovani_kurzu" + ], + "license": "cc-by-sa-40", + "license_code": "cc0", + "links": [ + "#definice_github_actions", + "#informace_o_tvem_repozitari", + "#nahrani_do_vlastniho_forku", + "#pridani_kurzu_na_nauc_se_python", + "#pull_request", + "#upravovani_kurzu", + "https://github.com/", + "https://github.com/pyvec/naucse-python", + "https://github.com/pyvec/naucse-python/blob/main/.github/workflows/main.yml", + "https://github.com/pyvec/naucse.python.cz/blob/master/courses.yml", + "https://naucse.python.cz/", + "naucse:page?lesson=git/branching", + "naucse:page?lesson=git/git-collaboration-2in1", + "naucse:static?filename=naucse_fork.png", + "naucse:static?filename=view-all-branches.png" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/meta/submitting-a-run/index.md", + "title": "Přidání kurzu na Nauč se Python", + "vars": {} + } + }, + "source_file": "lessons/meta/submitting-a-run/info.yml", + "static_files": { + "naucse_fork.png": { + "path": "submitting-a-run/naucse_fork.png" + }, + "view-all-branches.png": { + "path": "submitting-a-run/view-all-branches.png" + } + }, + "title": "Přidání kurzu na Nauč se Python" + }, + "micropython/mini-workshop": { + "pages": { + "index": { + "attribution": [ + "Napsal Petr Viktorin, 2017.", + "Ikonky textového editoru a terminálu jsou\nze sady Adwaita\nz GNOME Project.", + "Semafor je založen na\nanimaci\nod Matia na Wikipedii." + ], + "content": { + "path": "mini-workshop/index.html" + }, + "ids": [ + "alarm", + "barvy", + "blikani", + "cyklus", + "cyklus_s_podminkou", + "kalkulacka", + "konec", + "matematika", + "motorek", + "otevreni_komunikacniho_kanalu", + "otevreni_prikazove_radky", + "program", + "semafor", + "svetylko", + "tlacitko", + "vyrazy" + ], + "license": "cc-by-sa-40", + "links": [ + "#alarm", + "#barvy", + "#blikani", + "#cyklus", + "#cyklus_s_podminkou", + "#kalkulacka", + "#konec", + "#matematika", + "#motorek", + "#otevreni_komunikacniho_kanalu", + "#otevreni_prikazove_radky", + "#program", + "#semafor", + "#svetylko", + "#tlacitko", + "#vyrazy", + "naucse:page?lesson=micropython/mini-workshop&page=organizers", + "naucse:static?filename=icon_gedit.png", + "naucse:static?filename=icon_terminal.png", + "naucse:static?filename=semafor.gif" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/micropython/mini-workshop/index.md", + "title": "Workshop MicroPythonu", + "vars": {} + }, + "organizers": { + "attribution": [ + "Napsal Petr Viktorin, 2017.", + "Ikonky textového editoru a terminálu jsou\nze sady Adwaita\nz GNOME Project.", + "Semafor je založen na\nanimaci\nod Matia na Wikipedii." + ], + "content": { + "path": "mini-workshop/organizers.html" + }, + "ids": [ + "firmware", + "nakupni_seznam", + "pokyny_pro_organizatory", + "priprava_hardwaru", + "priprava_pocitace" + ], + "license": "cc-by-sa-40", + "links": [ + "#firmware", + "#nakupni_seznam", + "#pokyny_pro_organizatory", + "#priprava_hardwaru", + "#priprava_pocitace", + "https://getfedora.org/", + "https://github.com/adafruit/ampy", + "https://github.com/espressif/esptool", + "https://micropython.org/download#esp8266", + "https://www.aliexpress.com/wholesale?SearchText=3+pin+WS2812+connector", + "https://www.aliexpress.com/wholesale?SearchText=dupont+jumper+cable+m-m", + "https://www.aliexpress.com/wholesale?SearchText=impact+switch+module+dupont", + "https://www.aliexpress.com/wholesale?SearchText=mini+breadboard+170", + "https://www.aliexpress.com/wholesale?SearchText=nodemcu+v2+esp8266+ch340", + "https://www.aliexpress.com/wholesale?SearchText=sg92r", + "https://www.aliexpress.com/wholesale?SearchText=ws2812+strip", + "https://www.gme.cz/led-kulate-pouzdro?tech_par%5B103%5D%5B%5D=18997&showFilter=103", + "naucse:page?lesson=beginners/install-editor&page=gedit", + "naucse:static?filename=boot.py", + "naucse:static?filename=module_detail.jpg", + "naucse:static?filename=module_full.jpg" + ], + "slug": "organizers", + "solutions": [], + "source_file": "lessons/micropython/mini-workshop/organizers.md", + "title": "Pokyny pro organizátory", + "vars": {} + } + }, + "source_file": "lessons/micropython/mini-workshop/info.yml", + "static_files": { + "boot.py": { + "path": "mini-workshop/boot.py" + }, + "icon_gedit.png": { + "path": "mini-workshop/icon_gedit.png" + }, + "icon_terminal.png": { + "path": "mini-workshop/icon_terminal.png" + }, + "module_detail.jpg": { + "path": "mini-workshop/module_detail.jpg" + }, + "module_full.jpg": { + "path": "mini-workshop/module_full.jpg" + }, + "semafor.gif": { + "path": "mini-workshop/semafor.gif" + } + }, + "title": "Workshop MicroPythonu" + }, + "projects/asteroids": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2015-2017." + ], + "content": { + "path": "asteroids/index.html" + }, + "ids": [ + "asteroidy", + "dokonceni_a_rozsireni", + "hra_typu_asteroids", + "kolize", + "utok", + "vesmirna_lod" + ], + "license": "cc-by-sa-40", + "links": [ + "#asteroidy", + "#dokonceni_a_rozsireni", + "#hra_typu_asteroids", + "#kolize", + "#utok", + "#vesmirna_lod", + "http://kenney.nl", + "http://opengameart.org/content/smoke-particle-assets", + "http://opengameart.org/content/space-shooter-redux", + "http://pyglet.readthedocs.org/en/latest/programming_guide/text.html", + "https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset", + "https://docs.python.org/3/tutorial/datastructures.html#sets", + "https://en.wikipedia.org/wiki/Asteroids_%28video_game%29", + "https://en.wikipedia.org/wiki/Wraparound_%28video_games%29", + "https://github.com/pyvec/cheatsheets/blob/master/sets/sets-cs.pdf", + "https://pyglet.readthedocs.io/en/latest/modules/math.html#pyglet.math.Mat4", + "https://pyglet.readthedocs.io/en/latest/programming_guide/migration.html?highlight=matrix#window-projection-and-cameras", + "https://pyglet.readthedocs.io/en/latest/programming_guide/shapes.html", + "https://pythonhosted.org/pyglet/api/pyglet.graphics.Batch-class.html", + "naucse:page?lesson=beginners/inheritance", + "naucse:static?filename=screenshot.png" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/projects/asteroids/index.md", + "title": "Asteroids", + "vars": {} + } + }, + "source_file": "lessons/projects/asteroids/info.yml", + "static_files": { + "screenshot.png": { + "path": "asteroids/screenshot.png" + } + }, + "title": "Asteroids" + }, + "projects/github-api": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Brno napsal Petr Viktorin, 2015-2017." + ], + "content": { + "path": "github-api/index.html" + }, + "ids": [ + "api_githubu", + "autorizace", + "interakce", + "requests", + "uzivatelsky_ucet", + "webove_api" + ], + "license": "cc-by-sa-40", + "links": [ + "#api_githubu", + "#autorizace", + "#interakce", + "#requests", + "#uzivatelsky_ucet", + "#webove_api", + "https://api.github.com/emojis", + "https://developer.github.com/v3/", + "https://docs.github.com/en/rest/reference/activity#star-a-repository-for-the-authenticated-user", + "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods", + "https://github.com", + "https://github.com/pyvec/naucse.python.cz/stargazers", + "https://github.com/settings/tokens", + "naucse:page?lesson=intro/json" + ], + "slug": "index", + "solutions": [], + "source_file": "lessons/projects/github-api/index.md", + "title": "Github API", + "vars": {} + } + }, + "source_file": "lessons/projects/github-api/info.yml", + "static_files": {}, + "title": "Github API" + }, + "projects/pong": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Praha napsal Oskar Hollmann s použitím kódu Petra Viktorina, 2016." + ], + "content": { + "path": "pong/index.html" + }, + "ids": [ + "dynamika_hry", + "konstanty_a_stav_hry", + "odrazeni_micku", + "pohyb_palek", + "pong", + "rozehrani", + "solution-0", + "solution-1", + "solution-2", + "solution-3", + "solution-4", + "solution-5", + "vstup_od_uzivatele", + "vykresleni_hraci_plochy", + "zaver" + ], + "license": "cc-by-sa-40", + "links": [ + "#dynamika_hry", + "#konstanty_a_stav_hry", + "#odrazeni_micku", + "#pohyb_palek", + "#pong", + "#rozehrani", + "#vstup_od_uzivatele", + "#vykresleni_hraci_plochy", + "#zaver", + "https://en.wikipedia.org/wiki/Atari,_Inc.", + "https://en.wikipedia.org/wiki/Pong", + "https://www.youtube.com/watch?v=fiShX2pTz9A", + "naucse:page?lesson=intro/pyglet", + "naucse:solution?solution=0", + "naucse:solution?solution=1", + "naucse:solution?solution=2", + "naucse:solution?solution=3", + "naucse:solution?solution=4", + "naucse:solution?solution=5", + "naucse:static?filename=pong.png", + "naucse:static?filename=pong.py" + ], + "slug": "index", + "solutions": [ + { + "content": "
def vykresli():\n    ...\n    nakresli_obdelnik(\n        pozice_mice[0] - VELIKOST_MICE // 2,\n        pozice_mice[1] - VELIKOST_MICE // 2,\n        VELIKOST_MICE,\n        VELIKOST_MICE)\n
" + }, + { + "content": "

Existuje víc řešení.\nTřeba se vykreslit obdélník dvojnásobnou šířkou, ale na hraně okna takže\nho bude vidět jen půl:

\n
def vykresli():\n    ...\n    # palky - udelame si seznam souradnic palek, a pro kazdou dvojici souradnic\n    # v tom seznamu palku vykreslime\n    for x, y in [(0, pozice_palek[0]), (SIRKA, pozice_palek[1])] :\n        nakresli_obdelnik(\n            x - TLOUSTKA_PALKY,\n            y - DELKA_PALKY // 2,\n            TLOUSTKA_PALKY * 2,\n            DELKA_PALKY)\n
" + }, + { + "content": "
def vykresli():\n    ...\n    # prerusovana pulici cara - slozena ze spousty malych obdelnicku\n    for y in range(DELKA_PULICI_CARKY // 2, VYSKA, DELKA_PULICI_CARKY * 2):\n        nakresli_obdelnik(\n            SIRKA // 2 - 1,\n            y,\n            2,\n            DELKA_PULICI_CARKY)\n
" + }, + { + "content": "
def vykresli():\n    ...\n    # A nakonec vypiseme skore obou hracu\n    nakresli_text(\n        str(skore[0]),\n        x=ODSAZENI_TEXTU,\n        y=VYSKA - ODSAZENI_TEXTU - VELIKOST_FONTU,\n        pozice_x='left',\n    )\n\n    nakresli_text(\n        str(skore[1]),\n        x=SIRKA - ODSAZENI_TEXTU,\n        y=VYSKA - ODSAZENI_TEXTU - VELIKOST_FONTU,\n        pozice_x='right',\n    )\n
" + }, + { + "content": "
from pyglet.window import key\n...\ndef stisk_klavesy(symbol, modifikatory):\n    if symbol == key.W:\n        stisknute_klavesy.add(('nahoru', 0))\n    if symbol == key.S:\n        stisknute_klavesy.add(('dolu', 0))\n    if symbol == key.UP:\n        stisknute_klavesy.add(('nahoru', 1))\n    if symbol == key.DOWN:\n        stisknute_klavesy.add(('dolu', 1))\n\n\ndef pusteni_klavesy(symbol, modifikatory):\n    if symbol == key.W:\n        stisknute_klavesy.discard(('nahoru', 0))\n    if symbol == key.S:\n        stisknute_klavesy.discard(('dolu', 0))\n    if symbol == key.UP:\n        stisknute_klavesy.discard(('nahoru', 1))\n    if symbol == key.DOWN:\n        stisknute_klavesy.discard(('dolu', 1))\n...\n
" + }, + { + "content": "
import random\n...\ndef reset():\n    pozice_mice[0] = SIRKA // 2\n    pozice_mice[1] = VYSKA // 2\n\n    # x-ova rychlost - bud vpravo, nebo vlevo\n    if random.randint(0, 1):\n        rychlost_mice[0] = RYCHLOST\n    else:\n        rychlost_mice[0] = -RYCHLOST\n    # y-ova rychlost - uplne nahodna\n    rychlost_mice[1] = random.uniform(-1, 1) * RYCHLOST\n\n# nastavit vychozi stav pro start hry\nreset()\n
" + } + ], + "source_file": "lessons/projects/pong/index.md", + "title": "Praktické cvičení: Pong", + "vars": {} + } + }, + "source_file": "lessons/projects/pong/info.yml", + "static_files": { + "pong.png": { + "path": "pong/pong.png" + }, + "pong.py": { + "path": "pong/pong.py" + } + }, + "title": "Praktické cvičení: Pong" + }, + "projects/snake": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies Ostrava napsal Lumr Balhar, 2018." + ], + "content": { + "path": "snake/index.html" + }, + "ids": [ + "ctverecky_ven_grafiku_sem", + "housenka", + "hra_typu_had", + "jen_at_ji_hlavne_ze_mu_chutna", + "logika_hry_afaze_projektu", + "nacteni_vsech_obrazku_ze_slozky", + "nenechme_ho_utect", + "optimalizace_uklid", + "ovladani_pomoci_klavesnice", + "rozpohybovani_hada", + "solution-0", + "vyber_spravnych_obrazku", + "vykresleni_hada", + "zprikazove_radky_do_graficke_aplikace" + ], + "license": "cc-by-sa-40", + "links": [ + "#ctverecky_ven_grafiku_sem", + "#housenka", + "#hra_typu_had", + "#jen_at_ji_hlavne_ze_mu_chutna", + "#logika_hry_afaze_projektu", + "#nacteni_vsech_obrazku_ze_slozky", + "#nenechme_ho_utect", + "#optimalizace_uklid", + "#ovladani_pomoci_klavesnice", + "#rozpohybovani_hada", + "#vyber_spravnych_obrazku", + "#vykresleni_hada", + "#zprikazove_radky_do_graficke_aplikace", + "https://docs.python.org/3/library/pathlib.html", + "https://en.wikipedia.org/wiki/Snake_(video_game_genre)", + "naucse:page?lesson=beginners/list", + "naucse:page?lesson=intro/pyglet", + "naucse:solution?solution=0", + "naucse:static?filename=apple.png", + "naucse:static?filename=coords.svg", + "naucse:static?filename=green.png", + "naucse:static?filename=screenshot-cat.png", + "naucse:static?filename=screenshot-final.png", + "naucse:static?filename=snake-tiles.png", + "naucse:static?filename=snake-tiles.zip" + ], + "slug": "index", + "solutions": [ + { + "content": "
from pathlib import Path\n\nimport pyglet\n\nTILES_DIRECTORY = Path('snake-tiles')\n\nsnake_tiles = {}\nfor path in TILES_DIRECTORY.glob('*.png'):\n    snake_tiles[path.stem] = pyglet.image.load(path)\n\nprint(snake_tiles)\n
" + } + ], + "source_file": "lessons/projects/snake/index.md", + "title": "Snake", + "vars": {} + } + }, + "source_file": "lessons/projects/snake/info.yml", + "static_files": { + "apple.png": { + "path": "snake/apple.png" + }, + "coords.svg": { + "path": "snake/coords.svg" + }, + "green.png": { + "path": "snake/green.png" + }, + "screenshot-cat.png": { + "path": "snake/screenshot-cat.png" + }, + "screenshot-final.png": { + "path": "snake/screenshot-final.png" + }, + "snake-tiles.png": { + "path": "snake/snake-tiles.png" + }, + "snake-tiles.zip": { + "path": "snake/snake-tiles.zip" + } + }, + "title": "Snake" + }, + "snake/drawing": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies CZ napsal Petr Viktorin, 2018." + ], + "content": { + "path": "drawing/index.html" + }, + "ids": [ + "housenka", + "jak_vybrat_ctverecky", + "krmeni", + "logicke_a_zobrazovaci_souradnice", + "nacitani_kousku_hada", + "nakresli_mi_hada", + "reprezentace_hada", + "sazeni_ctverecku", + "sazeni_hada", + "solution-0", + "solution-1", + "solution-2", + "solution-3", + "solution-4" + ], + "license": "cc-by-sa-40", + "links": [ + "#housenka", + "#jak_vybrat_ctverecky", + "#krmeni", + "#logicke_a_zobrazovaci_souradnice", + "#nacitani_kousku_hada", + "#nakresli_mi_hada", + "#reprezentace_hada", + "#sazeni_ctverecku", + "#sazeni_hada", + "https://docs.python.org/3/library/pathlib.html", + "naucse:solution?solution=0", + "naucse:solution?solution=1", + "naucse:solution?solution=2", + "naucse:solution?solution=3", + "naucse:solution?solution=4", + "naucse:static?filename=apple.png", + "naucse:static?filename=caterpillar.png", + "naucse:static?filename=coords-blocks.svg", + "naucse:static?filename=coords-px.svg", + "naucse:static?filename=coords.svg", + "naucse:static?filename=green.png", + "naucse:static?filename=red.png", + "naucse:static?filename=screenshot-dir.png", + "naucse:static?filename=snake-tiles.png", + "naucse:static?filename=snake-tiles.zip" + ], + "slug": "index", + "solutions": [ + { + "content": "
import pyglet\n\nTILE_SIZE = 64\ngreen_image = pyglet.image.load('green.png')\n\nwindow = pyglet.window.Window()\n\n@window.event\ndef on_draw():\n    window.clear()\n    green_image.blit(4 * TILE_SIZE, 5 * TILE_SIZE,\n                     width=TILE_SIZE, height=TILE_SIZE)\n\npyglet.app.run()\n
" + }, + { + "content": "
import pyglet\n\nTILE_SIZE = 64\n\nsnake = [(1, 2), (2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (4, 5)]\n\ngreen_image = pyglet.image.load('green.png')\n\nwindow = pyglet.window.Window()\n\n@window.event\ndef on_draw():\n    window.clear()\n    for x, y in snake:\n        green_image.blit(x * TILE_SIZE, y * TILE_SIZE,\n                        width=TILE_SIZE, height=TILE_SIZE)\n\npyglet.app.run()\n
" + }, + { + "content": "
import pyglet\n\nTILE_SIZE = 64\n\nsnake = [(1, 2), (2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (4, 5)]\nfood = [(2, 0), (5, 1), (1, 4)]\n\nred_image = pyglet.image.load('apple.png')\ngreen_image = pyglet.image.load('green.png')\n\nwindow = pyglet.window.Window()\n\n@window.event\ndef on_draw():\n    window.clear()\n    for x, y in snake:\n        green_image.blit(x * TILE_SIZE, y * TILE_SIZE,\n                        width=TILE_SIZE, height=TILE_SIZE)\n    for x, y in food:\n        red_image.blit(x * TILE_SIZE, y * TILE_SIZE,\n                        width=TILE_SIZE, height=TILE_SIZE)\n\npyglet.app.run()\n
" + }, + { + "content": "
from pathlib import Path\n\nimport pyglet\n\nTILES_DIRECTORY = Path('snake-tiles')\n\nsnake_tiles = {}\nfor path in TILES_DIRECTORY.glob('*.png'):\n    snake_tiles[path.stem] = pyglet.image.load(path)\n\nprint(snake_tiles)\n
" + }, + { + "content": "
from pathlib import Path\n\nimport pyglet\n\nTILE_SIZE = 64\nTILES_DIRECTORY = Path('snake-tiles')\n\nsnake = [(1, 2), (2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (4, 5)]\nfood = [(2, 0), (5, 1), (1, 4)]\n\nred_image = pyglet.image.load('apple.png')\nsnake_tiles = {}\nfor path in TILES_DIRECTORY.glob('*.png'):\n    snake_tiles[path.stem] = pyglet.image.load(path)\n\nwindow = pyglet.window.Window()\n\n@window.event\ndef on_draw():\n    window.clear()\n    pyglet.gl.glEnable(pyglet.gl.GL_BLEND)\n    pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)\n    for x, y in snake:\n        snake_tiles['tail-head'].blit(\n            x * TILE_SIZE, y * TILE_SIZE, width=TILE_SIZE, height=TILE_SIZE)\n    for x, y in food:\n        red_image.blit(\n            x * TILE_SIZE, y * TILE_SIZE, width=TILE_SIZE, height=TILE_SIZE)\n\npyglet.app.run()\n
" + } + ], + "source_file": "lessons/snake/drawing/index.md", + "title": "Kreslení hada", + "vars": {} + } + }, + "source_file": "lessons/snake/drawing/info.yml", + "static_files": { + "apple.png": { + "path": "drawing/apple.png" + }, + "caterpillar.png": { + "path": "drawing/caterpillar.png" + }, + "coords-blocks.svg": { + "path": "drawing/coords-blocks.svg" + }, + "coords-px.svg": { + "path": "drawing/coords-px.svg" + }, + "coords.svg": { + "path": "drawing/coords.svg" + }, + "green.png": { + "path": "drawing/green.png" + }, + "red.png": { + "path": "drawing/red.png" + }, + "screenshot-dir.png": { + "path": "drawing/screenshot-dir.png" + }, + "screenshot-finished.png": { + "path": "drawing/screenshot-finished.png" + }, + "screenshot-initial.png": { + "path": "drawing/screenshot-initial.png" + }, + "snake-tiles.png": { + "path": "drawing/snake-tiles.png" + }, + "snake-tiles.zip": { + "path": "drawing/snake-tiles.zip" + }, + "snake-tiles/bottom-bottom.png": { + "path": "drawing/snake-tiles/bottom-bottom.png" + }, + "snake-tiles/bottom-dead.png": { + "path": "drawing/snake-tiles/bottom-dead.png" + }, + "snake-tiles/bottom-head.png": { + "path": "drawing/snake-tiles/bottom-head.png" + }, + "snake-tiles/bottom-left.png": { + "path": "drawing/snake-tiles/bottom-left.png" + }, + "snake-tiles/bottom-right.png": { + "path": "drawing/snake-tiles/bottom-right.png" + }, + "snake-tiles/bottom-tongue.png": { + "path": "drawing/snake-tiles/bottom-tongue.png" + }, + "snake-tiles/bottom-top.png": { + "path": "drawing/snake-tiles/bottom-top.png" + }, + "snake-tiles/left-bottom.png": { + "path": "drawing/snake-tiles/left-bottom.png" + }, + "snake-tiles/left-dead.png": { + "path": "drawing/snake-tiles/left-dead.png" + }, + "snake-tiles/left-head.png": { + "path": "drawing/snake-tiles/left-head.png" + }, + "snake-tiles/left-left.png": { + "path": "drawing/snake-tiles/left-left.png" + }, + "snake-tiles/left-right.png": { + "path": "drawing/snake-tiles/left-right.png" + }, + "snake-tiles/left-tongue.png": { + "path": "drawing/snake-tiles/left-tongue.png" + }, + "snake-tiles/left-top.png": { + "path": "drawing/snake-tiles/left-top.png" + }, + "snake-tiles/right-bottom.png": { + "path": "drawing/snake-tiles/right-bottom.png" + }, + "snake-tiles/right-dead.png": { + "path": "drawing/snake-tiles/right-dead.png" + }, + "snake-tiles/right-head.png": { + "path": "drawing/snake-tiles/right-head.png" + }, + "snake-tiles/right-left.png": { + "path": "drawing/snake-tiles/right-left.png" + }, + "snake-tiles/right-right.png": { + "path": "drawing/snake-tiles/right-right.png" + }, + "snake-tiles/right-tongue.png": { + "path": "drawing/snake-tiles/right-tongue.png" + }, + "snake-tiles/right-top.png": { + "path": "drawing/snake-tiles/right-top.png" + }, + "snake-tiles/tail-bottom.png": { + "path": "drawing/snake-tiles/tail-bottom.png" + }, + "snake-tiles/tail-dead.png": { + "path": "drawing/snake-tiles/tail-dead.png" + }, + "snake-tiles/tail-head.png": { + "path": "drawing/snake-tiles/tail-head.png" + }, + "snake-tiles/tail-left.png": { + "path": "drawing/snake-tiles/tail-left.png" + }, + "snake-tiles/tail-right.png": { + "path": "drawing/snake-tiles/tail-right.png" + }, + "snake-tiles/tail-tongue.png": { + "path": "drawing/snake-tiles/tail-tongue.png" + }, + "snake-tiles/tail-top.png": { + "path": "drawing/snake-tiles/tail-top.png" + }, + "snake-tiles/top-bottom.png": { + "path": "drawing/snake-tiles/top-bottom.png" + }, + "snake-tiles/top-dead.png": { + "path": "drawing/snake-tiles/top-dead.png" + }, + "snake-tiles/top-head.png": { + "path": "drawing/snake-tiles/top-head.png" + }, + "snake-tiles/top-left.png": { + "path": "drawing/snake-tiles/top-left.png" + }, + "snake-tiles/top-right.png": { + "path": "drawing/snake-tiles/top-right.png" + }, + "snake-tiles/top-tongue.png": { + "path": "drawing/snake-tiles/top-tongue.png" + }, + "snake-tiles/top-top.png": { + "path": "drawing/snake-tiles/top-top.png" + } + }, + "title": "Kreslení hada" + }, + "snake/logic": { + "pages": { + "index": { + "attribution": [ + "Pro PyLadies CZ napsal Petr Viktorin, 2018." + ], + "content": { + "path": "logic/index.html" + }, + "ids": [ + "a_to_je_vse", + "konec", + "krmeni", + "logika_hry", + "nekonecna_magicka_klec", + "nove_jidlo", + "ovladani", + "rozhybejme_hada", + "solution-0", + "solution-1", + "solution-10", + "solution-11", + "solution-2", + "solution-3", + "solution-4", + "solution-5", + "solution-6", + "solution-7", + "solution-8", + "solution-9", + "ven_se_stavem", + "vylepseni_ovladani", + "zatim_dobry_ted_ale_narazime" + ], + "license": "cc-by-sa-40", + "links": [ + "#a_to_je_vse", + "#konec", + "#krmeni", + "#logika_hry", + "#nekonecna_magicka_klec", + "#nove_jidlo", + "#ovladani", + "#rozhybejme_hada", + "#ven_se_stavem", + "#vylepseni_ovladani", + "#zatim_dobry_ted_ale_narazime", + "https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations", + "https://en.wikipedia.org/wiki/Torus#Topology", + "naucse:solution?solution=0", + "naucse:solution?solution=1", + "naucse:solution?solution=10", + "naucse:solution?solution=11", + "naucse:solution?solution=2", + "naucse:solution?solution=3", + "naucse:solution?solution=4", + "naucse:solution?solution=5", + "naucse:solution?solution=6", + "naucse:solution?solution=7", + "naucse:solution?solution=8", + "naucse:solution?solution=9" + ], + "slug": "index", + "solutions": [ + { + "content": "
from pathlib import Path\n\nimport pyglet\n\nfrom had import State\n\nTILE_SIZE = 64\nTILES_DIRECTORY = Path('snake-tiles')\n\nred_image = pyglet.image.load('apple.png')\nsnake_tiles = {}\nfor path in TILES_DIRECTORY.glob('*.png'):\n    snake_tiles[path.stem] = pyglet.image.load(path)\n\nwindow = pyglet.window.Window()\n\nstate = State()\n\n@window.event\ndef on_draw():\n    window.clear()\n    pyglet.gl.glEnable(pyglet.gl.GL_BLEND)\n    pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)\n    for x, y in state.snake:\n        source = 'tail'     # (Tady případně je nějaké\n        dest = 'head'       #  složitější vybírání políčka)\n        snake_tiles[source + '-' + dest].blit(\n            x * TILE_SIZE, y * TILE_SIZE, width=TILE_SIZE, height=TILE_SIZE)\n    for x, y in state.food:\n        red_image.blit(\n            x * TILE_SIZE, y * TILE_SIZE, width=TILE_SIZE, height=TILE_SIZE)\n\ndef move(dt):\n    state.move()\n\npyglet.clock.schedule_interval(move, 1/6)\n\npyglet.app.run()\n
" + }, + { + "content": "

Ve funkci move je potřeba jinak nastavit proměnné new_x a new_y:

\n
        new_x = old_x\n        new_y = old_y + 1\n
" + }, + { + "content": "
        new_x = old_x\n        new_y = old_y - 1\n
" + }, + { + "content": "
    def move(self):\n        old_x, old_y = self.snake[-1]\n        dir_x, dir_y = self.snake_direction\n        new_x = old_x + dir_x\n        new_y = old_y + dir_y\n\n        # Kontrola vylezení z hrací plochy\n        if new_x < 0:\n            exit('GAME OVER')\n        if new_y < 0:\n            exit('GAME OVER')\n        if new_x >= self.width:\n            exit('GAME OVER')\n        if new_y >= self.height:\n            exit('GAME OVER')\n\n        new_head = new_x, new_y\n        self.snake.append(new_head)\n        del self.snake[0]\n
" + }, + { + "content": "
        # Kontrola vylezení z hrací plochy\n        if new_x < 0:\n            new_x = self.width - 1\n        if new_y < 0:\n            new_y = self.height - 1\n        if new_x >= self.width:\n            new_x = 0\n        if new_y >= self.height:\n            new_y = 0\n
" + }, + { + "content": "

Do metody move, kamkoli za řádek který nastavuje new_head.

" + }, + { + "content": "
    def move(self):\n        old_x, old_y = self.snake[-1]\n        dir_x, dir_y = self.snake_direction\n        new_x = old_x + dir_x\n        new_y = old_y + dir_y\n\n        # Kontrola vylezení z hrací plochy\n        new_x = new_x % self.width\n        new_y = new_y % self.height\n\n        new_head = new_x, new_y\n        self.snake.append(new_head)\n        if new_head in self.food:\n            self.food.remove(new_head)\n        else:\n            del self.snake[0]\n
" + }, + { + "content": "
    def add_food(self):\n        x = random.randrange(self.width)\n        y = random.randrange(self.height)\n        position = x, y\n        self.food.append(position)\n
" + }, + { + "content": "

V metodě __init__ se dá místo nastavení self.food na seznam s pozicemi\njídla napsat:

\n
        self.food = []\n        self.add_food()\n        self.add_food()\n

Pak budou na začátku hry na hada čekat dvě náhodné jídla.

" + }, + { + "content": "

Kód patří do metody move, hned za nastavení proměnné new_head.

" + }, + { + "content": "
    \n
  • „Prvotní nastavení atributu“ do metody __init__.
  • \n
  • „Kontrola, jestli had narazil“ do move místo původní kontroly,\nkdy se hra ukončila pomocí exit().
  • \n
  • „Zabránění pohybu“ na úplný začátek metody move (příkaz return\nokamžitě ukončí provádění metody).
  • \n
  • „Grafická indikace“ do ui.py, za sekci pro vybírání obrázku pro kousek\nhada.
  • \n
" + }, + { + "content": "

had.py:

\n
import random\n\nclass State:\n    def __init__(self):\n        self.food = []\n        self.add_food()\n        self.add_food()\n        self.snake = [(0, 0), (1, 0)]\n        self.snake_direction = 0, 1\n        self.width = 10\n        self.height = 10\n        self.snake_alive = True\n        self.queued_directions = []\n\n    def move(self):\n        if self.queued_directions:\n            new_direction = self.queued_directions[0]\n            del self.queued_directions[0]\n            old_x, old_y = self.snake_direction\n            new_x, new_y = new_direction\n            if (old_x, old_y) != (-new_x, -new_y):\n                self.snake_direction = new_direction\n\n        if not self.snake_alive:\n            return\n\n        old_x, old_y = self.snake[-1]\n        dir_x, dir_y = self.snake_direction\n        new_x = old_x + dir_x\n        new_y = old_y + dir_y\n\n        new_x = new_x % self.width\n        new_y = new_y % self.height\n\n        new_head = new_x, new_y\n        if new_head in self.snake:\n            self.snake_alive = False\n        self.snake.append(new_head)\n\n        if new_head in self.food:\n            self.food.remove(new_head)\n            self.add_food()\n        else:\n            del self.snake[0]\n\n    def add_food(self):\n        for try_number in range(100):\n            x = random.randrange(self.width)\n            y = random.randrange(self.height)\n            position = x, y\n            if (position not in self.snake) and (position not in self.food):\n                self.food.append(position)\n                return\n

ui.py:

\n
from pathlib import Path\n\nimport pyglet\n\nfrom had import State\n\nTILE_SIZE = 64\nTILES_DIRECTORY = Path('snake-tiles')\n\nred_image = pyglet.image.load('apple.png')\nsnake_tiles = {}\nfor path in TILES_DIRECTORY.glob('*.png'):\n    snake_tiles[path.stem] = pyglet.image.load(path)\n\nwindow = pyglet.window.Window()\n\nstate = State()\nstate.width = window.width // TILE_SIZE\nstate.height = window.height // TILE_SIZE\n\n\n@window.event\ndef on_draw():\n    window.clear()\n    pyglet.gl.glEnable(pyglet.gl.GL_BLEND)\n    pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)\n    for x, y in state.snake:\n        source = 'tail'     # (Tady případně je nějaké\n        dest = 'head'       #  složitější vybírání políčka)\n        if dest == 'head' and not state.snake_alive:\n            dest = 'dead'\n        snake_tiles[source + '-' + dest].blit(\n            x * TILE_SIZE, y * TILE_SIZE, width=TILE_SIZE, height=TILE_SIZE)\n    for x, y in state.food:\n        red_image.blit(\n            x * TILE_SIZE, y * TILE_SIZE, width=TILE_SIZE, height=TILE_SIZE)\n\n\n@window.event\ndef on_key_press(symbol, mod):\n    if symbol == pyglet.window.key.LEFT:\n        new_direction = -1, 0\n    if symbol == pyglet.window.key.RIGHT:\n        new_direction = 1, 0\n    if symbol == pyglet.window.key.DOWN:\n        new_direction = 0, -1\n    if symbol == pyglet.window.key.UP:\n        new_direction = 0, 1\n    state.queued_directions.append(new_direction)\n\n\ndef move(dt):\n    state.move()\n\n\npyglet.clock.schedule_interval(move, 1/6)\n\npyglet.app.run()\n
" + } + ], + "source_file": "lessons/snake/logic/index.md", + "title": "Had – Logika hry", + "vars": {} + } + }, + "source_file": "lessons/snake/logic/info.yml", + "static_files": {}, + "title": "Had – Logika hry" + } + }, + "long_description": "

Seznam udržovaných lekcí bez ladu a skladu.

\n

Jednotlivé kurzy jsou poskládané z těchto materiálů\n(a doplněné jinými).

", + "sessions": [ + { + "materials": [ + { + "lesson_slug": "advanced/generators", + "title": "Generátory", + "type": "lesson" + } + ], + "serial": "1", + "slug": "advanced", + "source_file": "lessons", + "title": "`advanced`" + }, + { + "materials": [ + { + "lesson_slug": "beginners/and-or", + "title": "Nebo anebo a", + "type": "lesson" + }, + { + "lesson_slug": "beginners/basic-functions", + "title": "Užitečné funkce", + "type": "lesson" + }, + { + "lesson_slug": "beginners/circular-imports", + "title": "Cyklické importy", + "type": "lesson" + }, + { + "lesson_slug": "beginners/class", + "title": "Třídy", + "type": "lesson" + }, + { + "lesson_slug": "beginners/cmdline", + "title": "Příkazová řádka", + "type": "lesson" + }, + { + "lesson_slug": "beginners/comparisons", + "title": "Porovnávání", + "type": "lesson" + }, + { + "lesson_slug": "beginners/def", + "title": "Definice funkcí", + "type": "lesson" + }, + { + "lesson_slug": "beginners/dict", + "title": "Slovníky", + "type": "lesson" + }, + { + "lesson_slug": "beginners/dict-with-list-values", + "title": "Víc hodnot v jednom záznamu slovníku", + "type": "lesson" + }, + { + "lesson_slug": "beginners/exceptions", + "title": "Výjimky", + "type": "lesson" + }, + { + "lesson_slug": "beginners/expressions", + "title": "Vyhodnocování výrazů", + "type": "lesson" + }, + { + "lesson_slug": "beginners/files", + "title": "Soubory", + "type": "lesson" + }, + { + "lesson_slug": "beginners/first-steps", + "title": "První krůčky", + "type": "lesson" + }, + { + "lesson_slug": "beginners/fstring", + "title": "Šablony (formátovací řetězce)", + "type": "lesson" + }, + { + "lesson_slug": "beginners/functions", + "title": "Funkce", + "type": "lesson" + }, + { + "lesson_slug": "beginners/hello-world", + "title": "První program", + "type": "lesson" + }, + { + "lesson_slug": "beginners/inheritance", + "title": "Dědičnost", + "type": "lesson" + }, + { + "lesson_slug": "beginners/install", + "title": "Instalace Pythonu", + "type": "lesson" + }, + { + "lesson_slug": "beginners/install-editor", + "title": "Instalace editoru", + "type": "lesson" + }, + { + "lesson_slug": "beginners/interfaces", + "title": "Rozhraní", + "type": "lesson" + }, + { + "lesson_slug": "beginners/list", + "title": "Seznamy", + "type": "lesson" + }, + { + "lesson_slug": "beginners/local-variables", + "title": "Lokální proměnné", + "type": "lesson" + }, + { + "lesson_slug": "beginners/main-module", + "title": "Spouštěcí moduly a Negativní testy", + "type": "lesson" + }, + { + "lesson_slug": "beginners/micropython", + "title": "MicroPython", + "type": "lesson" + }, + { + "lesson_slug": "beginners/modules", + "title": "Moduly", + "type": "lesson" + }, + { + "lesson_slug": "beginners/nested-list", + "title": "Vnořené seznamy", + "type": "lesson" + }, + { + "lesson_slug": "beginners/nested-traceback", + "title": "Chybové hlášky ze zanořených funkcí", + "type": "lesson" + }, + { + "lesson_slug": "beginners/prefer-return", + "title": "Vrátit nebo vypsat?", + "type": "lesson" + }, + { + "lesson_slug": "beginners/print", + "title": "Print a chybové hlášky", + "type": "lesson" + }, + { + "lesson_slug": "beginners/range", + "title": "Range - sekvence čísel", + "type": "lesson" + }, + { + "lesson_slug": "beginners/reassignment", + "title": "Přepisování proměnných", + "type": "lesson" + }, + { + "lesson_slug": "beginners/recursion", + "title": "Rekurze", + "type": "lesson" + }, + { + "lesson_slug": "beginners/str", + "title": "Zápis řetězců", + "type": "lesson" + }, + { + "lesson_slug": "beginners/str-index-slice", + "title": "Výběr z řetězců", + "type": "lesson" + }, + { + "lesson_slug": "beginners/str-methods", + "title": "Řetězcové funkce a metody", + "type": "lesson" + }, + { + "lesson_slug": "beginners/testing", + "title": "Testování", + "type": "lesson" + }, + { + "lesson_slug": "beginners/tuple", + "title": "N-tice", + "type": "lesson" + }, + { + "lesson_slug": "beginners/variables", + "title": "Proměnné", + "type": "lesson" + }, + { + "lesson_slug": "beginners/venv-setup", + "title": "Nastavení prostředí", + "type": "lesson" + }, + { + "lesson_slug": "beginners/while", + "title": "Cyklus While", + "type": "lesson" + }, + { + "lesson_slug": "beginners/with", + "title": "Kontext: with a finally", + "type": "lesson" + }, + { + "lesson_slug": "beginners/zip-enumerate", + "title": "Iterátory n-tic", + "type": "lesson" + } + ], + "serial": "2", + "slug": "beginners", + "source_file": "lessons", + "title": "`beginners`" + }, + { + "materials": [ + { + "lesson_slug": "fast-track/http", + "title": "HTTP – Jak funguje Internet", + "type": "lesson" + }, + { + "lesson_slug": "fast-track/install", + "title": "Vytvoření virtuáního prostředí", + "type": "lesson" + }, + { + "lesson_slug": "fast-track/yaml", + "title": "O formátu YAML", + "type": "lesson" + } + ], + "serial": "3", + "slug": "fast-track", + "source_file": "lessons", + "title": "`fast-track`" + }, + { + "materials": [ + { + "lesson_slug": "git/basics", + "title": "Git", + "type": "lesson" + }, + { + "lesson_slug": "git/branching", + "title": "Větvení v Gitu", + "type": "lesson" + }, + { + "lesson_slug": "git/collaboration", + "title": "Spolupráce a Open source", + "type": "lesson" + }, + { + "lesson_slug": "git/git-collaboration-2in1", + "title": "Spolupráce a Git", + "type": "lesson" + }, + { + "lesson_slug": "git/ignoring", + "title": "Ignorování souborů", + "type": "lesson" + }, + { + "lesson_slug": "git/install", + "title": "Instalace Gitu", + "type": "lesson" + } + ], + "serial": "4", + "slug": "git", + "source_file": "lessons", + "title": "`git`" + }, + { + "materials": [ + { + "lesson_slug": "intro/async", + "title": "AsyncIO", + "type": "lesson" + }, + { + "lesson_slug": "intro/click", + "title": "Click – Rozhraní pro příkazovou řádku", + "type": "lesson" + }, + { + "lesson_slug": "intro/cython", + "title": "Cython", + "type": "lesson" + }, + { + "lesson_slug": "intro/decorators", + "title": "Dekorátory", + "type": "lesson" + }, + { + "lesson_slug": "intro/deployment", + "title": "Deployment webových aplikací", + "type": "lesson" + }, + { + "lesson_slug": "intro/distribution", + "title": "Moduly", + "type": "lesson" + }, + { + "lesson_slug": "intro/docs", + "title": "Dokumentace", + "type": "lesson" + }, + { + "lesson_slug": "intro/flask", + "title": "Flask", + "type": "lesson" + }, + { + "lesson_slug": "intro/json", + "title": "JSON", + "type": "lesson" + }, + { + "lesson_slug": "intro/magic", + "title": "Magie", + "type": "lesson" + }, + { + "lesson_slug": "intro/micropython", + "title": "MicroPython", + "type": "lesson" + }, + { + "lesson_slug": "intro/mypy", + "title": "Mypy — statická typová kontrola pro Python", + "type": "lesson" + }, + { + "lesson_slug": "intro/notebook", + "title": "Notebook", + "type": "lesson" + }, + { + "lesson_slug": "intro/numpy", + "title": "NumPy", + "type": "lesson" + }, + { + "lesson_slug": "intro/pandas", + "title": "Pandas", + "type": "lesson" + }, + { + "lesson_slug": "intro/pyglet", + "title": "Grafika", + "type": "lesson" + }, + { + "lesson_slug": "intro/pyqt", + "title": "GUI v Pythonu", + "type": "lesson" + }, + { + "lesson_slug": "intro/requests", + "title": "Requests – Weboví klienti", + "type": "lesson" + }, + { + "lesson_slug": "intro/testing", + "title": "Testování", + "type": "lesson" + }, + { + "lesson_slug": "intro/turtle", + "title": "Želva a cykly", + "type": "lesson" + } + ], + "serial": "5", + "slug": "intro", + "source_file": "lessons", + "title": "`intro`" + }, + { + "materials": [ + { + "lesson_slug": "meta/installing-naucse", + "title": "Lokální instalace Nauč se Python", + "type": "lesson" + }, + { + "lesson_slug": "meta/local-run", + "title": "Vytvoření lokálního kurzu", + "type": "lesson" + }, + { + "lesson_slug": "meta/submitting-a-run", + "title": "Přidání kurzu na Nauč se Python", + "type": "lesson" + } + ], + "serial": "6", + "slug": "meta", + "source_file": "lessons", + "title": "`meta`" + }, + { + "materials": [ + { + "lesson_slug": "micropython/mini-workshop", + "title": "Workshop MicroPythonu", + "type": "lesson" + } + ], + "serial": "7", + "slug": "micropython", + "source_file": "lessons", + "title": "`micropython`" + }, + { + "materials": [ + { + "lesson_slug": "projects/asteroids", + "title": "Asteroids", + "type": "lesson" + }, + { + "lesson_slug": "projects/github-api", + "title": "Github API", + "type": "lesson" + }, + { + "lesson_slug": "projects/pong", + "title": "Praktické cvičení: Pong", + "type": "lesson" + }, + { + "lesson_slug": "projects/snake", + "title": "Snake", + "type": "lesson" + } + ], + "serial": "8", + "slug": "projects", + "source_file": "lessons", + "title": "`projects`" + }, + { + "materials": [ + { + "lesson_slug": "snake/drawing", + "title": "Kreslení hada", + "type": "lesson" + }, + { + "lesson_slug": "snake/logic", + "title": "Had – Logika hry", + "type": "lesson" + } + ], + "serial": "9", + "slug": "snake", + "source_file": "lessons", + "title": "`snake`" + } + ], + "source_file": "lessons", + "title": "Kanonické lekce" + } +} \ No newline at end of file diff --git a/lessons/cython/index.html b/lessons/cython/index.html new file mode 100644 index 00000000..7c92d0c0 --- /dev/null +++ b/lessons/cython/index.html @@ -0,0 +1,630 @@ +

Dnes budeme potřebovat do virtuálního prostředí nainstalovat tyto knihovny:

+
$ python -m pip install --upgrade pip
+$ python -m pip install notebook numpy cython pytest pytest-profiling
+

Také je potřeba nainstalovat překladač jazyka C +a hlavičkové soubory Pythonu:

+
    +
  • Na Linuxu bude stačit nainstalovat balíčky gcc +a python3-devel (Fedora) nebo python3-dev (Ubuntu/Debian).
  • +
  • Na Windows se řiďte instrukcemi pro vaši verzi Pythonu +na Python wiki.
  • +
+
+

C API +# +

+

Mluvíme-li o „Pythonu“, máme často na mysli jak jazyk samotný, tak i interpret, +program, který programy v tomto jazyce umí spouštět. +Správně je ale „Python“ pouze jméno jazyka. +Interpretů tohoto jazyka je více, například:

+
    +
  • CPython, referenční implementace napsaná v C; interpret, který spouštíme příkazem python3
  • +
  • PyPy, implementace zaměřená na rychlost, napsaná v Pythonu
  • +
  • MicroPython, implementace pro mikroprocesory a zařízení s minimem paměti
  • +
  • Jython, implementace napsaná v Javě, která umožňuje využívat javovské třídy
  • +
  • IronPython, napsaný v C#, s integrací do .NET
  • +
  • Batavia, Brython, pyjs – různé pokusy o integraci do JavaScriptu
  • +
+

Jednotlivé interprety se liší v detailech jako jsou přesnost reálných čísel, +vypisování chybových hlášek, řazení záznamů ve slovnících nebo přístup +k interním strukturám interpretu. +Správně napsaný pythonní program by neměl na takových detailech záviset, pokud +není k nekompatibilitě mezi interprety dobrý důvod.

+

Někdy to ale je potřeba, a dnešní přednáška bude specifická pro CPython +a přímé využití jeho API pro jazyk C.

+

Rychlost +# +

+

Častý důvod proč sáhnout k C API je rychlost: CPython je celkem pomalý. +Tradiční metoda optimalizace je zjistit, které části jsou kritické, a přepsat +je do C. +Využijí se tak výhody obou jazyků: Python pro rychlý vývoj, snadné +prototypování a přehlednost kódu, a C pro rychlost.

+

Když je náš program příliš pomalý, je potřeba ho optimalizovat. +První krok k tomu je vždy zkontrolovat, co zabírá více času, než by mělo. +K tomu se dá použít nástroj profile ze standardní knihovny, který vypíše +tabulku počtu volání jednotlivých funkcí a času v nich stráveného:

+
$ python -m profile -s cumtime program.py
+

Profilovat běh pytest testů se dá jednoduše pomocí modulu pytest-profiling:

+
$ python -m pip install pytest-profiling
+$ python -m pytest --profile
+

Když máme představu o tom, co nás brzdí, můžeme začít přepisovat do C způsoby +popsanými níže.

+

Jiná možnost, jak program zrychlit, je ho pustit, tak jak je, pod interpretem +PyPy, který obsahuje optimalizovaný překladač. To je ale jiná kapitola.

+

Externí knihovny +# +

+

Druhý důvod, proč programátoři používají C API, je použití knihoven, které mají +rozhraní pro C. +Takových knihoven existuje mnoho – pokud není něco specifické pro určitý jazyk, +často se to dá volat i z C.

+

Pro práci s externími knihovnami se dá použít C API nebo vestavěný modul +ctypes, ale v dnešní době je dobré místo toho použít CFFI, knihovnu +která funguje i s PyPy (a teoreticky jinými implementacemi).

+

CPython +# +

+

Třetí důvod, proč použít C API, je práce s CPythonem samotným. +Když člověk zabředne do složitého problému, může na CPython pustit C debugger +jako gdb nebo Valgrind, prozkoumat potíže na nižší úrovni +a zjistit, kde přesně se chyba nachází.

+

Modul v C +# +

+

Pojďme začít příkladem. +Vytvořte si následující soubor, který implementuje rozšíření +(importovatelný modul) s jednou funkcí.

+

(Nebudeme chtít, abyste podobný kód uměli napsat, ale měli byste být schopní +porozumět tomu, co dělá.)

+

demo.c:

+
#include <Python.h>
+
+PyDoc_STRVAR(
+    mod_docstring, 
+    "Demo extension module with a Python wrapper for the system(3) function");
+
+static PyObject *demo_system(PyObject *self, PyObject *args){
+    const char *command;
+    int retval;
+
+    /* Parse the given arguments: expect one string, convert to char* */
+    if (!PyArg_ParseTuple(args, "s", &command)) {
+        /* Error handling: if PyArg_ParseTuple returns zero, return NULL */
+        return NULL;
+    }
+
+    /* Call the C function */
+    retval = system(command);
+
+    /* Return result as Python int (error handling built in) */
+    return PyLong_FromLong(retval);
+}
+
+/* List of all methods in the module */
+static PyMethodDef DemoMethods[] = {
+    {"system",  demo_system, METH_VARARGS,
+            PyDoc_STR("Execute a shell command.")},
+    {NULL, NULL, 0, NULL}        /* Sentinel */
+};
+
+/* Module specification */
+static struct PyModuleDef demo_module = {
+   PyModuleDef_HEAD_INIT,
+   "demo",          /* name of module */
+   mod_docstring,   /* dosctring (may be NULL) */
+   0,               /* size of per-interpreter state of the module */
+   DemoMethods,     /* list of methods */
+};
+
+
+/* Module entrypoint */
+PyMODINIT_FUNC
+PyInit_demo(void)
+{
+    return PyModuleDef_Init(&demo_module);
+}
+

Z tohoto souboru by měla být patrná struktura podobných rozšíření: +máme funkci (demo_system), která převádí objekty Pythonu +na datové typy C, volá samotnou funkci a výsledek převádí zpět na pythonní +objekt.

+

Dále máme pole záznamů o funkcích (DemoMethods), kde je ke každé funkci +přiřazeno jméno, dokumentační řetězec a způsob volání (v našem případě +METH_VARARGS, tedy volání s proměnným počtem nepojmenovaných argumentů, +podobně jako bychom v Pythonu napsali def system(*args)).

+

Další potřebná proměnná, demo_module, obsahuje informace o modulu: +jméno, dokumentační řetězec a seznam funkcí. +Kdybychom potřebovali kromě funkcí definovat i třídy nebo konstanty, +zde bychom pomocí slotů definovali funkci, která modul +inicializuje, t.j. má podobnou funkci jako __init__ u třídy v Pythonu.

+

Poslední část je funkce PyInit, jediná která není definována jako static, +takže jediná, která je exportována jako API knihovny, kterou vytváříme. +Až bude Python tento modul importovat, najde tuto funkci podle jména, spustí ji +a podle vrácené struktury typu PyModuleDef vytvoří pythonní objekt s modulem.

+

Překlad +# +

+

Abychom mohli takovýto modul naimportovat, musíme ho nejdřív přeložit a sestavit +z něj sdílenou knihovnu – soubor .so (nebo .dll) – s názvem modulu: +buď jen demo.so, nebo i s identifikací architektury a verze Pythonu, +např. demo.cpython-35m-x86_64-linux-gnu.so. +(Výhoda delších názvů je v tom, že v jednom adresáři může být víc modulů pro +různé architektury a že se Python nebude snažit načíst nekompatibilní moduly.)

+

Překlad je nutné provést se správnými přepínači a volbami, nejlépe takovými, +s jakými byl sestaven samotný Python.

+

Pro zjednodušení tohoto procesu můžeme použít setuptools: do nám už známého +souboru setup.py přidáme argument ext_modules se seznamem rozšiřovacích modulů. +Podrobný popis třídy Extension je v dokumentaci; nám bude stačit +jen jméno a seznam zdrojových souborů:

+

setup.py:

+
from setuptools import setup, Extension
+
+module1 = Extension(
+    'demo',
+    sources=['demo.c'],
+)
+
+setup(
+    name = 'demo',
+    version = '0.1',
+    description = 'Demo package',
+    ext_modules = [module1]
+)
+

Příkazy python setup.py sdist a python setup.py install budou fungovat jako normálně, +jen je na instalaci potřeba překladač jazyka C.

+

Aby uživatelé překladač mít nemuseli, můžeme nainstalovat knihovnu wheel (python -m pip install wheel) a pak příkazem python setup.py bdist_wheel vygenerovat tzv. wheel archiv, +např. dist/demo-0.1-cp35-cp35m-linux_x86_64.whl. Tento archiv jde nahrát na PyPI a následně +nainstalovat, ovšem jen na architektuře a verzi Pythonu, pro které byl vytvořen.

+

Existuje způsob, jak vytvořit co nejvíce platformě nezávislý linuxový wheel. +Jedná se o platformu nazvanou manulinux1, což je ve zkratce velmi stará verze +Linuxu (CentOS 5), na které se wheely vytvoří, aby šly použít na různých +novějších i relativně starých distribucích. Pro tvorbu wheelů se používá +Docker obraz manylinux, +vývojáři samozřejmě nepoužívají pro vývoj CentOS 5 (tedy většina ne).

+

Zajímavým nástrojem, který stojí za zmínku, je cibuildwheel. +Zjednodušuje tvorbu wheelů pro Linux, macOS i Windows pomocí +CI služeb Travis CI a AppVeyor.

+

Wheels jdou vytvářet i pro moduly tvořené jen pythonním kódem. +Nejsou pak vázané na verzi a architekturu. +Jejich výhoda oproti sdist archivům spočívá v tom, že se rychleji instalují.

+

Alternativa k instalaci, alespoň pro lokální vývoj, je rozšíření jen přeložit a dát do +aktuálního adresáře (nebo jakéhokoli jiného adresáře, odkud se importují moduly). +K tomu slouží příkaz python setup.py build_ext --inplace. +Pozor na to, že po každé změně zdrojového kódu je potřeba rozšíření znovu přeložit.

+

Příkaz python setup.py develop bude fungovat jako dřív (používá build_ext --inplace), +jen je opět potřeba příkaz po každé změně znovu spustit.

+

PyObject +# +

+

Podívejme se teď na základní mechanismy interpretu CPython.

+

Základní datová struktura, která reprezentuje jakýkoli objekt Pythonu, je PyObject +(dokumentace, +definice). +Skládá se ze dvou prvků:

+
typedef struct _object {
+    Py_ssize_t ob_refcnt;
+    struct _typeobject *ob_type;
+} PyObject;
+

První je počet referencí (reference count), který se dá popsat jako počet míst, +ze kterých je možné k tomuto objektu přistoupit. +Když objekt uložíme do proměnné nebo do seznamu, zvýší se počet referencí o 1. +Když seznam nebo proměnná zanikne (nebo náš objekt přepíšeme jiným), +počet referencí se zase sníží. +Když počet referencí dosáhne nuly, znamená to, že se k objektu už nedá dostat a Python ho +uvolní z paměti.

+

Druhý prvek struktury PyObject je ukazatel na typ. +Typ je pythonní objekt (class), který definuje chování třídy objektů: operátory, +atributy a metody, které ten objekt má.

+

Struktura PyObject slouží jako hlavička, za kterou pak následují data interpretovaná podle +typu daného objektu. +Například pythonní objekt typu float vypadá následovně:

+
typedef struct {
+    PyObject ob_base;
+    double ob_fval;
+} PyFloatObject;
+

...tedy struktura PyObject, za kterou je v paměti číselná hodnota.

+

Seznamy obsahují za hlavičkou např. velikost a (ukazatel na) pole ukazatelů na jednotlivé +prvky. +Podobně objekty typu int (které mají v Pythonu neomezený rozsah) mají délku a pole +jednotlivých 30bitových „číslic“. +NumPy matice mají metadata (velikost, typ, popis rozložení v paměti) a ukazatel na pole hodnot.

+

To základní, co potřebujeme vědět, je, že na úrovni C je každý pythonní objekt reprezentován +jako struktura počtu referencí, ukazatele na typ a dat specifických pro daný typ.

+

Reference counting +# +

+

Tak jako v C je důležité správně alokovat a dealokovat paměť, při tvorbě rozšíření do CPythonu +je třeba správně pracovat s referencemi: ke každému Py_INCREF (přičtení 1 k počtu referencí) +je potřeba později zavolat Py_DECREF (odečtení 1 a případné uvolnění objektu). +Jakákoli práce s objektem se smí provádět jen mezi INCREF a příslušným DECREF.

+

Platí konvence, že argumenty funkcí se předávají jako tzv. borrowed reference: o počitadlo +se stará volající a v průběhu volané funkce se objekt dá používat. +Pokud bychom ale argument potřebovali i po skončení volané funkce (např. si ho uložíme +do globální proměnné), je potřeba mu počitadlo zvýšit (a po skončení práce zase snížit).

+

V našem modulu demo přebíráme jako parametr n-tici. +Zodpovědnost zavolat na tuto n-tici Py_DECREF má ale volající, ne my. +Zavoláním funkce PyArg_ParseTuple získáme char*, který ale můžeme používat jen v rámci naší +funkce: po jejím skončení může volající argumenty funkce uvolnit, a tím řetězec zrušit.

+

Funkce, které vracejí pythonní objekty, předpokládají, že na vrácenou hodnotu provede DECREF volající. +V modulu demo voláme funkci PyLong_FromLong, která vytvoří nové pythonní číslo. +Za vzniklou referenci naše funkce přebírá zodpovědnost, je tedy na nás, abychom se postarali +o zavolání Py_DECREF. +Vrácením výsledku tuto zodpovědnost ale předáváme na funkci, která volá tu naši.

+

Hodnoty a výjimky +# +

+

Další konvence, kterou většina funkcí v C API dodržuje, je způsob vracení výjimek.

+

Funkce, které vrací pythonní objekty, na úrovni C vrací PyObject*. +Nastane-li výjimka, objekt výjimky se zaznamená do globální (přesněji, thread-local) +proměnné a funkce vrátí NULL.

+

V našem modulu demo voláme funkci PyArg_ParseTuple, která může vyvolat výjimku: typicky +TypeError kvůli nesprávnému počtu nebo typu argumentů. +V takovém případě tato funkce výjimku zaznamená a vrátí NULL. +Naší funkci system už stačí vrátit NULL, protože víme, že výjimka už je zaznamenaná.

+

Další funkce, která může neuspět, je PyLong_FromLong. +Vzhledem k tomu, že její výsledek rovnou vracíme, není potřeba úspěch kontrolovat – vrátíme +buď správnou hodnotu nebo NULL se zaznamenanou výjimkou.

+

GIL +# +

+

Poslední omezení, kterého si autor rozšíření musí být vědom, je Global Interpreter Lock. +Stručně řečeno, s objekty PyObject* může pracovat pouze jedno vlákno. +Toto vlákno drží globální zámek, který čas od času odemkne a znovu se pokusí zamknout, +aby mohly běžet i ostatní vlákna.

+

Díky GIL je vícevláknové programování v Pythonu relativně bezpečné: nemůže např. nastat souběh +(race condition), kdy by se nastavilo počitadlo referencí na špatnou hodnotu. +Na druhou stranu tento zámek ale omezuje paralelismus, a tedy i rychlost programu.

+

Globální zámek se dá odemknout v situacích, kdy nepracujeme s PyObject* a nevoláme pythonní kód. +Například čtení ze souboru nebo sítě ostatní vlákna neblokuje. +Stejně tak maticové operace v NumPy typicky nedrží GIL zatímco počítají na úrovni C nebo Fortranu.

+

Cython +# +

+

Teď, když víme jak to všechno funguje, se můžeme podívat na způsob, jak rozšíření psát +jednoduše. +C API se totiž dá použít nejen z C, ale z jakéhokoli jazyka, který umí volat funkce se +stejnými konvencemi, např. C++ (s pomocí extern C). +Další způsob, jak použít C API ale nepsat C, je použít překladač z příjemnějšího jazyka do C.

+

Jeden takový jazyk je Cython (neplést s CPython).

+

Cython je jazyk podobný Pythonu, který ale lze přeložit na C a dále optimalizovat.

+

Cython si nainstalujte pomocí příkazu:

+
$ python -m pip install cython
+

Kompilace Pythonu +# +

+

Když chceme převést modul z Pythonu do Cythonu, nejjednodušší začátek je přejmenovat soubor .py +na .pyx, aby bylo jasné, že jde o jiný jazyk, který nepůjde naimportovat přímo.

+

Jazyky Python a Cython nejsou 100% kompatibilní, ale zvláště u kódu, který pracuje hlavně s +čísly, se nekompatibilita neprojeví. +Vývojáři Cythonu považují každou odchylku od specifikace jazyka za chybu, kterou je nutno opravit.

+

Jako příklad můžete použít tuto naivní implementaci celočíselného a maticového násobení. +Uložte si ji jako matmul.py:

+
import numpy
+
+def intmul(a, b):
+    result = a * b
+    return result
+
+def matmul(a, b):
+    n = a.shape[0]
+    m = a.shape[1]
+    if b.shape[0] != m:
+        raise ValueError('incompatible sizes')
+    p = b.shape[1]
+    result = numpy.zeros((n, p))
+    for i in range(n):
+        for j in range(p):
+            for k in range(m):
+                x = a[i, k]
+                y = b[k, j]
+                result[i, j] += x * y
+    return result
+

Stáhněte si testy a zkontrolujte, že prochází.

+

Potom soubor přejmenujte na matmul.pyx.

+

Výsledek bychom mohli převést na C pomocí příkazu cython -3 matmul.pyx, čímž +vznikne matmul.c. Ten můžeme přeložit výše uvedeným způsobem.

+

Jednodušší varianta je použít Cython v setup.py. +Pro naše účely bude setup.py s Cythonem a NumPy vypadat takto:

+
from setuptools import setup
+from Cython.Build import cythonize
+import numpy
+
+setup(
+    name='matmul',
+    ext_modules=cythonize('matmul.pyx', language_level=3),
+    include_dirs=[numpy.get_include()],
+    setup_requires=[
+        'Cython',
+        'NumPy',
+    ],
+    install_requires=[
+        'NumPy',
+    ],
+)
+

V případě problémech s nefungujícím include_dirs na systému macOS +použijte komplikovanější variantu:

+
from distutils.extension import Extension
+...
+ext_modules = cythonize([Extension('matmul', ['matmul.pyx'],
+                                   include_dirs=[numpy.get_include()])],
+                        language_level=3)
+

Po zadání python setup.py develop nebo python setup.py build_ext --inplace atp. +se modul matmul.pyx zkompiluje s použitím nainstalovaného NumPy a bude připraven na použití. +(Zkontrolujte, že testy prochází i se zkompilovaným modulem.)

+

Nevýhoda tohoto přístupu je, že k spuštění takového setup.py je již potřeba +mít nainstalovaný cython a numpy. +Instalace z archivu sdist se tedy nemusí povést – je potřeba uživatelům říct, +že dané moduly už musí mít nainstalované. +Tento problém aktuálně řeší PyPA (správci pip a setuptools).

+

Instalace z archivů wheel by měla být bezproblémová.

+

Anotace +# +

+

Kód, který takto vznikne, není o moc rychlejší než původní Python. +Je to tím, že sekvence příkazů ve funkci je sice převedená do C a přeložená do strojového kódu, +ale každá operace pracuje s generickými pythonními objekty, takže musí pro každé číslo +číslo z matice zkonstruovat pythonní objekt, vyhledat implementaci sčítání pro dvě celá čísla, +a výsledek převést zpět na int64 a uložit do matice.

+

Na situaci se můžeme podívat pomocí přepínače --annotate:

+
$ cython -3 --annotate matmul.pyx
+

To vygeneruje soubor matmul.html, kde jsou potencionálně pomalé operace vysvíceny žlutě. +Ke každému řádku se navíc dá kliknutím ukázat odpovídající kód v C (který bývá docela složitý, +protože řeší věci jako zpětnou kompatibilitu a ošetřování chyb, a navíc používá hodně pomocných +maker).

+

Obecně nebývá problém mít „žluté“ ty řádky, které se ve funkci provádí pouze jednou. +Ale v cyklech, zvláště těch třikrát zanořených, se autor rozšíření typicky snaží žlutým řádkům +vyhnout. +Nejjednodušší způsob, jak toho docílit, je doplnění statických informací o typech.

+

Doplnění typů +# +

+

Začneme u funkce intmul, kde doplníme informaci o tom, že parametry a a b a proměnná +result jsou typu int. +Parametrům stačí doplnit typ podobně jako v C, ostatní lokální proměnné potřebují definici pomocí +příkazu cdef:

+
def intmul(int a, int b):
+    cdef int result
+    result = a * b
+    return result
+

Teď bude funkce nepatrně rychlejší, ale také méně obecná: nejde jí násobit řetězec číslem, +ale ani reálná čísla (float), a dokonce ani celá čísla, která se nevejdou do 64 bitů (příp. +jiné velikosti, dle systému). +Typ int v Cythonu je totiž int z C, ne ten neomezený z Pythonu.

+

Další věc, kterou můžeme udělat, je změnit příkaz def na cpdef a doplnit typ návratové +hodnoty:

+
cpdef int intmul(int a, int b):
+    cdef int result
+    result = a * b
+    return result
+

Tím se zbavíme nákladného převodu výsledku na PyObject. +Bohužel ale toto zrychlení pocítíme, jen když takovou funkci zavoláme +z jiné funkce napsané v Cythonu.

+

Tři typy funkcí +# +

+

Funkce jdou deklarovat třemi způsoby:

+
    +
  • def func(...): je funkce, která jde volat z Pythonu i z Cythonu, ale volání z Cythonu je pomalé (argumenty a výsledek se převádí na pythonní objekty a zpět),
  • +
  • cdef <type> func(...): je funkce, která jde volat pouze z Cythonu, ale volání je rychlé (pracuje se s C typy),
  • +
  • cpdef <type> func(...): je funkce, která se z Cythonu volá rychle, ale jde volat i z Pythonu (ve skutečnosti Cython vytvoří dva druhy této funkce).
  • +
+

Třídy +# +

+

Cython umožňuje vytvářet tzv. built-in třídy: stejný druh tříd jako je +např. str nebo int. +Práce s takovými třídami je rychlejší, ale mají pevně danou strukturu. +Ani jim ani jejich instancím nelze z Pythonu nastavovat nové atributy:

+
>>> "foo".bar = 3
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+AttributeError: 'str' object has no attribute 'bar'
+

Příklad definice built-in třídy:

+
cdef class Foo:
+    # Všechny členské proměnné musí být nadefinované tady
+    cdef int foo
+    ...
+
+    def __cinit__(self, int f):
+        # Inicializace třídy.
+        # Cython zajistí, že se tato funkce zavolá pouze jednou (na rozdíl
+        # od __init__, kterou lze z pythonního kódu zavolat kdykoli)
+        self.foo = f
+        ...
+
+    def __dealloc__(self):
+        # Deinicializace třídy
+        ...
+
+    cpdef int method(self):
+        ...
+        return self.foo
+

Více o definici tříd najdete v dokumentaci Cythonu.

+

Používání NumPy +# +

+

Pro funkci matmul můžeme nadefinovat číselné proměnné (n, m, p, i, j, k, x, y) +jako int, ale tím si moc nepomůžeme: většinu času program stráví vybíráním a ukládáním hodnot +z/do matic, a protože Cython nemá informace o tom, že jsou to NumPy matice, používá obecný +protokol pro pythonní kontejnery, takže se každá hodnota převede na pythonní objekt.

+

Je tedy potřeba říct Cythonu, že používáme NumPy matice. +Naštěstí v NumPy existuje integrace s Cythonem, takže můžeme na úrovni C „naimportovat“ +rozšíření pro NumPy:

+
cimport numpy
+

... a potom použít typ „dvourozměrná matice celých čísel“, který se v Cythonu jmenuje +numpy.ndarray[numpy.int64_t, ndim=2]. +Naše funkce tedy bude začínat takto:

+
cpdef numpy.ndarray[numpy.int64_t, ndim=2] matmul(
+        numpy.ndarray[numpy.int64_t, ndim=2] a,
+        numpy.ndarray[numpy.int64_t, ndim=2] b):
+    cdef numpy.ndarray[numpy.int64_t, ndim=2] result
+    ...
+

Kdybychom si nebyli jistí typem matice, můžeme si ho nadefinovat pomocí ctypedef:

+
ctypedef numpy.int64_t DATATYPE
+

...a pak používat tento alias. +Na maticové typy bohužel typedef zatím nefunguje.

+

Pro práci s maticí ASCII znaků lze použít typ numpy.int8_t, ale je třeba při zapisování přímo na konkrétní pozice zapisovat číselný typ char:

+
cdef numpy.ndarray[numpy.int8_t, ndim=2]  directions = numpy.full((h, w), b'#', dtype=('a', 1))
+directions[maze >= 0] = b' '  # Python level, using b' '
+directions[1, 2] == ord('x')  # C level, using char
+

Použití matrix[a, b] je v Cythonu rychlejší než matrix[a][b], protože se +uvnitř dějí jiné věci. Při použití matrix[a, b] u matice deklarované jako +dvourozměrné pole nějakého typu Cython přistoupí přímo k obsahu na úrovni +jazyka C. Při použití matrix[a][b] se ale dějí operace dvě, nejprve +matrix[a] vrací jeden řádek matice a až poté [b] vrací jeden prvek z +tohoto řádku. Obě operace probíhají na úrovni Pythonu a proto budou pomalejší +a při použití --annotate bude řádek s takovou operací označen žlutě.

+

Direktivy +# +

+

Anotací typů matic se naše demo maticového násobení dostalo skoro na úroveň +C, ale ne úplně: řádky, které pracují s maticemi, jsou ve výstupu --annotate +stále trochu žluté. +Cython totiž při každém přístupu k matici kontroluje, jestli nečteme nebo +nezapisujeme mimo pole a případně vyvolá IndexError.

+

Pokud víme – jako v našem případě – že je taková kontrola zbytečná, +můžeme Cythonu říct, aby ji nedělal. +Přístupy mimo pole pak způsobí nedefinované chování (většinou program spadne, +nebo hůř, bude pracovat se špatnými daty). +Kontrola se vypíná direktivou boundscheck, která se dá zadat dvěma hlavními +způsoby: dekorátorem:

+
@cython.boundscheck(False)
+cpdef funkce():
+    ...

... nebo příkazem with:

+
with cython.boundscheck(False):
+    ...

... případně i pro celý soubor, viz dokumentace.

+

Další zajímavá direktiva je cython.wraparound(False), která podobným způsobem +vypíná pythonní způsob indexování zápornými čísly: místo indexování od konce +s ní dostaneme nedefinované chování.

+

Seznam dalších direktiv najdete v dokumentaci.

+

Cython podporuje ještě blok with cython.nogil:, který je podobný direktivám, +ale dá se použít jen s with. +V rámci tohoto bloku je odemčený GIL (globální zámek). +Smí se použít, pouze pokud nepracujeme s pythonními objekty – například když +operujeme jen na obsahu už existujících maticí. +Opak je with cython.gil:, kterým zámek zase zamkneme – například když +potřebujeme vyhodit výjimku.

+

Struktury, ukazatele a dynamická alokace +# +

+

Přestože v Cythonu můžete používat pythonní n-tice, slovníky, seznamy a další +podobné nehomogenní typy, jejich použití je pomalé, protože vždy pracují +s pythonními objekty.

+

Pokud máte kód, který potřebuje dočasné pole takových záznamů, +je pro časově kritické části kódu lepší k problému přistoupit spíše „céčkovsky“, +přes alokaci paměti a ukazatele.

+

Následující příklad ukazuje, jak naplnit pole heterogenních záznamů:

+
# Import funkcí pro alokaci paměti – chovají se jako malloc() apod.
+from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
+
+# Definice struktury
+cdef struct coords:
+    int row
+    int column
+    char data
+
+MAXSIZE = ...
+
+def path(...):
+    # Definice ukazatele, přetypování
+    cdef coords * path = <coords *>PyMem_Malloc(MAXSIZE*sizeof(coords))
+    if path == NULL:
+        # nedostatek paměti
+        raise MemoryError()
+
+    cdef int used = 0
+    for ...:
+        ...
+
+        #
+        path[used] = coords(row, column, data)
+        used += 1
+
+    # pole můžeme používat
+    ...
+
+    # a musíme ho před vrácením předělat na list
+    lpath = []
+    cdef int i
+    for i in range(used):
+        lpath.append(path[i])
+
+    # a uvolnit
+    PyMem_Free(path)
+    return lpath
+

Pro homogenní pole ale doporučujeme spíše NumPy matice.

+

Následující příklad ukazuje, jak lze přiřazovat do struktur:

+
cdef struct coord:
+    float x
+    float y
+    float z
+
+cdef coord a = coord(0.0, 2.0, 1.5)
+
+cdef coord b = coord(x=0.0, y=2.0, z=1.5)
+
+cdef coord c
+
+c.x = 42.0
+c.y = 2.0
+c.z = 4.0
+
+cdef coord d = {'x':2.0,
+                'y':0.0,
+                'z':-0.75}
+

Použití knihoven z C +# +

+

Pro použití C knihoven z Pythonu je lepší použít CFFI. +Ale když už píšete kód v Cythonu +a potřebujete zavolat nějakou C funkci, můžete to udělat takto:

+
cdef extern from "stdlib.h":
+    int rand()
+    void srand(long int seedval)
+
+cdef extern from "time.h":
+    ctypedef long time_t
+    long int time(time_t *)
+
+srand(time(NULL))
+print(rand())
+

Deklarace můžete vložit přímo do .pyx souboru, ale pokud je chcete používat +z různých míst, pojmenujte soubor .pxd, to vám umožní na něj použít cimport.

+

Pro části standardní knihovny jsou takové deklarace již v Cythonu +předpřipravené, můžete tedy použít cimport rovnou:

+
from libc.stdlib cimport rand, srand
+from libc.time cimport time
+
+srand(time(NULL))
+print(rand())
+

Zkratky: pyximport a %%cython +# +

+

Pro interaktivní práci v Jupyter Notebook má Cython vlastní „magii“. +Na začátku Notebooku můžeme zadat:

+
%load_ext cython
+

a potom můžeme na začátku kterékoli buňky zadat %%cython:

+
%%cython
+
+cpdef int mul(int a, int b):
+    return a * b
+

Kód v takové buňce pak Notebook zkompiluje Cythonem a funkce/proměnné v něm +nadefinované dá k dispozici.

+

Můžeme použít i %%cython --annotate, což vypíše anotace přímo do Notebooku.

+

Další zkratka je modul pyximort, který dává možnost importovat moduly .pyx +přímo: hledají se podobně jako .py nebo .so a před importem se zkompilují. +Zapíná se to následovně:

+
import pyximport
+pyximport.install()
+
+import matmul
+

Video +# +

+

Před nedávnem měl Miro na Středisku unixových technologií nahrávanou ukázku přepsání +úlohy ruksaku z předmětu MI-PAA z Pythonu do Cythonu (včetně nepříjemného záseku a live +ukázky debugování problému). +Na video se můžete podívat, mohlo by vám prozradit spoustu tipů, které se vám mohou hodit +ke splnění úlohy. +K obsahu jen dodáme, že místo malloc a free je lepší použít PyMem_Malloc a +PyMem_Free z ukázky výše.

\ No newline at end of file diff --git a/lessons/intro/cython/static/test_matmul.py b/lessons/cython/test_matmul.py similarity index 100% rename from lessons/intro/cython/static/test_matmul.py rename to lessons/cython/test_matmul.py diff --git a/lessons/decorators/index.html b/lessons/decorators/index.html new file mode 100644 index 00000000..7d5ed6e4 --- /dev/null +++ b/lessons/decorators/index.html @@ -0,0 +1,367 @@ +

Dekorátory +# +

+

V této lekci se nebudeme věnovat žádné externí knihovně. Místo toho se seznámíme +s jednou vlastností Pythonu, kterou knihovny často využívají, a která obvykle +vypadá trochu magicky.

+

Touto vlastností jsou dekorátory.

+

Dekorátory se hodí tehdy, když potřebujeme upravit chování nějaké funkce, ale +nechceme ji přímo upravovat.

+

Teorie do začátku +# +

+

Co je to vlastně dekorátor? Dekorátor je vlastně jenom funkce, která dostane +jeden argument a vrátí jednu hodnotu. Je ale trochu speciální v tom, že jak +argument, tak návratová hodnota jsou zase jiné funkce.

+

Funkcím, které operují nad jinými funkcemi, říkáme funkce vyššího řádu.

+

Použití dekorátorů v kódu vypadá zhruba takto:

+
@dekorator
+def funkce():
+    pass
+

Tento zápis se zavináčem je jenom syntaktický cukr. Usnadňuje nám zápis, ale +chová se přesně stejně jako následující kód, na kterém je lépe vidět, že +dekorator je funkce:

+
def funkce():
+    pass
+funkce = dekorator(funkce)
+

Na řádku za zavináčem může být libovolný výraz, který po vyhodnocení vrátí +funkci, která má požadované rozhraní.

+

Přiklad 0 – registrace funkcí +# +

+

Jak už při programování bývá zvykem, náš první dekorátor nás pozdraví.

+

Začneme s jednoduchým programem, který definuje funkci pro pozdrav a zavolá ji.

+
def ahoj():
+    print("Ahoj")
+
+
+if __name__ == "__main__":
+    ahoj()
+

Do tohoto programu bychom rádi přidali další pozdravy, a zavolali je všechny.

+
def ahoj():
+    print("Ahoj")
+
+
+def nazdar():
+    print("Nazdar")
+
+
+if __name__ == "__main__":
+    ahoj()
+    nazdar()
+

Tento přístup ale povede k tomu, že by na konci byl dlouhý seznam pozdravů. +Můžeme si funkce rovnou uložit do seznamu a potom přes něj jenom iterovat.

+
def ahoj():
+    print("Ahoj")
+
+
+def nazdar():
+    print("Nazdar")
+
+
+if __name__ == "__main__":
+    funkce = [ahoj, nazdar]
+    for f in funkce:
+        f()
+

A jako poslední krok přidáme dekorátor, který nám bude funkce rovnou přidávat +do seznamu.

+
funkce = []
+
+
+def pridej_pozdrav(func):
+    funkce.append(func)
+    return func
+
+
+@pridej_pozdrav
+def ahoj():
+    print("Ahoj")
+
+
+@pridej_pozdrav
+def nazdar():
+    print("Nazdar")
+
+
+if __name__ == "__main__":
+    for f in funkce:
+        f()
+

Zkuste přidat ještě jeden pozdrav.

+

V tomto příkladu jde o docela zbytečné použití dekorátorů. Ukazuje ale +praktický způsob, jak řešit registraci funkcí. Stejné řešení používá +například knihovna flask pro definování webových služeb nebo click pro +vytváření příkazů pro terminál.

+

Příklad 1 – trasování volání funkcí +# +

+

Podívejme se třeba na tuto na pohled nevinnou funkci. Počítá, jak vypadá n-té +číslo ve Fibonacciho posloupnosti. Funguje docela pěkně, pokud jí nezadáme jako +argument příliš velké číslo. Na autorově počítači příliš velká čísla začínají +kolem 35.

+
def fib(x):
+    """Spočítá x-té číslo ve Fibonacciho posloupnosti."""
+    if x <= 1:
+        return x
+    return fib(x - 1) + fib(x - 2)
+

Napíšeme si jednoduchý dekorátor, který nám bude vypisovat informace o tom, co +se ve funkci děje.

+
def co_se_deje(func):
+    print("Aplikuju dekorátor")
+    return func
+
+
+@co_se_deje
+def fib(x):
+    """Spočítá x-té číslo ve Fibonacciho posloupnosti."""
+    if x <= 1:
+        return x
+    return fib(x - 1) + fib(x - 2)
+
+if __name__ == "__main__":
+    print(fib(4))
+

Tento dekorátor funkci nijak nemění. Akorát nám oznámí, že byl aplikovaný. V +těle dekorátoru ale můžeme nadefinovat novou funkci a vrátit ji.

+

Zkusme si to:

+
def co_se_deje(func):
+    def nahradni_funkce(x):
+        return "Spočítej si to sám!"
+
+    return nahradni_funkce
+

Nebo můžeme vrátit funkci, která akorát zavolá tu původní.

+
def co_se_deje(func):
+    def nahradni_funkce(x):
+        return func(x)
+
+    return nahradni_funkce
+

Pojďme vracenou funkci rozšířit tak, aby vypisovala informace o tom, co dělá.

+
def co_se_deje(func):
+    def nahradni_funkce(x):
+        print(f"Voláme {func.__name__}({x})")
+        return func(x)
+
+    return nahradni_funkce
+

Úkol: upravte dekorátor tak, aby vypisoval i vypočítanou hodnotu.

+
+

Řešení

+ + +
+ +

Tento dekorátor není úplně praktický. Pokud toho vypíše trochu víc, tak už se +v tom logu nikdo nevyzná. Myšlenka jako taková ovšem není úplně špatná. Kdyby +třeba dekorátor počítal, kolikrát se funkce spustí, a jak dlouho obvykle +trvá, mohl by nám pomoct najít místa pro optimalizaci.

+

Nápověda pro funkce +# +

+

Zkuste si v interaktivní konzoli Pythonu spustit následující příklad:

+
>>> help(print)
+Help on built-in function print in module builtins:
+
+print(...)
+    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
+
+    Prints the values to a stream, or to sys.stdout by default.
+    Optional keyword arguments:
+    file:  a file-like object (stream); defaults to the current sys.stdout.
+    sep:   string inserted between values, default a space.
+    end:   string appended after the last value, default a newline.
+    flush: whether to forcibly flush the stream.
+

Dostaneme krátkou nápovědu o tom, jak používat funkci print.

+

Odkud se tato nápověda bere? Z dokumentačního komentáře. Takže bychom měli +dostat pěknou nápovědu třeba i pro naši známou funkci fib.

+
>>> from fib import fib
+>>> help(fib)
+Help on function nahradni_funkce in module fib:
+
+nahradni_funkce(x)
+
+>>>
+

Něco je špatně. Protože jsme původní implementaci funkce fib pomocí +dekorátoru nahradili naší pomocnou funkcí, komentář se cestou ztratil. Mohli +bychom přidat dokumentační komentář k náhradní funkci, ale přece nebudeme +stejný kód kopírovat dvakrát.

+

Standardní knihovna má naštěstí možnost, jak to snadno opravit. V modulu +functools je definovaný dekorátor wraps, který umí zkopírovat dokumentační +komentář a jméno z jedné funkce do druhé.

+
import functools
+
+
+def co_se_deje(func):
+    @functools.wraps(func)
+    def nahradni_funkce(x):
+        pass
+
>>> from fib import fib
+>>> help(fib)
+Help on function fib in module fib:
+
+fib(x)
+    Spočítá x-té číslo ve Fibonacciho posloupnosti.
+
+>>>
+

Předávání všech argumentů. +# +

+

Při psaní dekorátorů je dobré myslet na to, jak moc univerzální by měly být. +Například náš co_se_deje momentálně funguje pouze pro funkce, které mají +jeden argument.

+

To je ale docela hloupé omezení. Stejně dobře bychom mohli chtít sledovat +volání jiné funkce, která má třeba argumentů víc.

+

Pokud dekorátor nepotřebuje vědět nic o argumentech funkce, je docela praktické +jej nadefinovat tak, aby byly prostě všechny předal dál, ať už jich je kolik +chce.

+

To můžeme udělat následovně:

+
def co_se_deje(func):
+    @functools.wraps(func)
+    def nahradni_funkce(*args, **kwargs):
+        print(f"Voláme {func.__name__}{args}")
+        vysledek = func(*args, **kwargs)
+        print(f"Výsledek {func.__name__}{args} = {vysledek}")
+        return vysledek
+    return nahradni_funkce
+

Do n-tice args posbíráme všechny poziční argumenty, do slovníku kwargs +všechny pojmenované argumenty. A při volání dekorované funkce je všechny zase +předáme dál.

+

Ve výstupu teď používáme pouze poziční argumenty. Přidání těch pojmenovaných je +cvičení pro čtenáře.

+

Příklad 2 – opakování HTTP požadavků +# +

+

Pokud náš program musí pracovat s nějakou externí službou nebo systémem, může +se stát, že komunikace mezi nimi nebude vždy bezproblémová. Pěkný příklad je +třeba stahování webové stránky se špatným připojením. S tím z Pythonu nic +udělat nemůžeme.

+

Můžeme ale zkusit požadavek zopakovat, pokud poznáme, že je to typ chyby, kde +opakování může pomoct.

+

Začneme s jednoduchým programem, který udělá HTTP požadavek.

+

Následující příklady používají knihovnu requests. Nainstalujte si ji, pokud ji +ve virtuálním prostředí ještě nemáte.

+
import requests
+
+def stahni():
+    """Stáhne stránku a něco s ní udělá."""
+    print("Stahuju stránku")
+    odpoved = requests.get("https://httpbin.org/status/200,400,500")
+    print(f"Dostali jsme {odpoved.status_code}")
+    odpoved.raise_for_status()
+    return "OK"
+
+
+if __name__ == "__main__":
+    stahni()
+

Použitá stránka náhodně odpoví jedním z vyjmenovaných kódu, takže ve dvou +třetinách případů bychom měli dostat chybu. Pokud požadavek zkusíme zopakovat, +máme dobrou šanci, že to projde.

+

Začneme s jednoduchým dekorátorem, který jenom zavolá funkci.

+
def opakuj_pri_neuspechu(func):
+    """Pokud volání funkce vyhodí výjimku, budeme ji ignorovat a zkusíme funkci
+    zavolat znovu.
+    """
+    @functools.wraps(func)
+    def nahradni_funkce(*args, **kwargs):
+        return func(*args, **kwargs)
+
+    return nahradni_funkce
+
+
+@opakuj_pri_neuspechu
+def stahni():
+    """Stáhne stránku a něco s ní udělá."""
+    print("Stahuju stránku")
+    odpoved = requests.get("https://httpbin.org/status/200,400,500")
+    print(f"Dostali jsme {odpoved.status_code}")
+    odpoved.raise_for_status()
+    return "OK"
+

Co by měla dělat naše náhradní funkce? Donekonečna bude zkoušet zavolat +dekorovanou funkci. Pokud se to podaří, vrátí její výsledek. Pokud dostaneme +výjimku requests.exceptions.HTTPError, chvilku počkáme, a půjdeme na další +pokus.

+
import functools
+import time
+
+import requests
+
+
+def opakuj_pri_neuspechu(func):
+    """Pokud volání funkce vyhodí výjimku, budeme ji ignorovat a zkusíme funkci
+    zavolat znovu.
+    """
+
+    @functools.wraps(func)
+    def nahradni_funkce(*args, **kwargs):
+        while True:
+            try:
+                return func(*args, **kwargs)
+            except requests.exceptions.HTTPError:
+                print("Chyba, zkusíme to znovu")
+                time.sleep(1)
+
+    return nahradni_funkce
+
+
+@opakuj_pri_neuspechu
+def stahni():
+    """Stáhne stránku a něco s ní udělá."""
+    print("Stahuju stránku")
+    odpoved = requests.get("https://httpbin.org/status/200,400,500")
+    print(f"Dostali jsme {odpoved.status_code}")
+    odpoved.raise_for_status()
+    return "OK"
+

Teď by program měl vypisovat, že se snaží stránku stáhnout několikrát, a +opakovat to tak dlouho, dokud se to nepodaří.

+

Co když ale potřebujeme opakování pokusů na více místech, ale chceme reagovat +na jiné výjimky?

+

Mohli bychom si nadefinovat nový dekorátor pro každý typ výjimky, kterou +chceme chytat. To zní jako hodně práce a duplicitního kódu.

+

Místo toho můžeme dekorátor upravit tak, aby přijímal argumenty, a pak mu +s jejich pomocí řekneme, kterou výjimku ošetřovat.

+

Výraz za @ musí při vyhodnocení vždy vracet funkci, která se chová jako +dekorátor. Takže musíme přidat jednu vrstvu do našich vnořených funkcí.

+

Funkce opakuj_pri_neuspechu je vlastně továrna na dekorátory. Vždy, když ji +zavoláme, vrátí nám funkci, která se chová podle našich potřeb a funguje jako +dekorátor.

+
import functools
+import time
+
+import requests
+
+
+def opakuj_pri_neuspechu(vyjimka):
+
+    def dekorator(func):
+
+        @functools.wraps(func)
+        def nahradni_funkce(*args, **kwargs):
+            while True:
+                try:
+                    return func(*args, **kwargs)
+                except vyjimka:
+                    print("Chyba, zkusíme to znovu")
+                    time.sleep(1)
+
+        return nahradni_funkce
+
+    return dekorator
+
+
+@opakuj_pri_neuspechu(requests.exceptions.HTTPError)
+def stahni():
+    """Stáhne stránku a něco s ní udělá."""
+    print("Stahuju stránku")
+    odpoved = requests.get("https://httpbin.org/status/200,400,500")
+    print(f"Dostali jsme {odpoved.status_code}")
+    odpoved.raise_for_status()
+    return "OK"
+
\ No newline at end of file diff --git a/lessons/def/index.html b/lessons/def/index.html new file mode 100644 index 00000000..4b333123 --- /dev/null +++ b/lessons/def/index.html @@ -0,0 +1,166 @@ +

Definice funkcí +# +

+

Dříve jsme +volali funkce, které napsal někdo jiný:

+
print('Ahoj světe!')
+

Dnes si ukážeme, jak psát funkce vlastní.

+

K čemu jsou funkce? +# +

+

Často se stává, že kód, který dělá nějakou jednoduchou věc, není úplně +jednoduchý. +Jako příklad uvedu nám už známý kód, který v určitém řetězci zamění znak +na dané pozici:

+
zacatek = slovo[:pozice]
+konec = slovo[pozice + 1:]
+nove_slovo = zacatek + novy_znak + konec
+

Z takového kódu není na první pohled jasné, co přesně dělá. +Zvlášť když kód použiješ ve složitějším programu.

+

Dá se to vyřešit komentářem: ten, kdo bude program číst, si může přečíst, +co to má dělat. Samotný složitější kód pak může ignorovat.

+
# Ve slově `slovo` zaměnit znak na pozici `pozice` za `novy_znak`;
+# výsledek bude v proměnné `nove_slovo`.
+zacatek = slovo[:pozice]
+konec = slovo[pozice + 1:]
+nove_slovo = zacatek + novy_znak + konec
+

Ještě lepší ale bude si vytvořit funkci, která tenhle složitější postup +provede. +Jakmile takovou funkci vytvoříš, ve složitějším programu pak můžeš místo kódu +výše psát jen:

+
nove_slovo = zamen(slovo, pozice, novy_znak)
+

Podobně fungují funkce, které už znáš: můžeš zavolat print(123), aniž bys +potřeboval/a znát jakékoli detaily postupu, kterým se číslo převede na +jednotlivé číslice a ty se pak vykreslí na obrazovce. +Nebo řekneš želvě forward(100) a nezatěžuješ se tím, jak si želva „pamatuje“ +svůj aktuální úhel natočení nebo jak se vlastně kreslí čára.

+

Funkce umožňuje pojmenovat nějaký kousek programu, který se pak dá +použít pomocí jména bez detailních znalostí toho, jak to vevnitř funguje.

+

Definice funkce +# +

+

Protože už znáš if a for, které mají jednořádkovou hlavičku a odsazené tělo +příkazu, neměl by ti zápis funkce připadat příliš zvláštní:

+
def zamen(slovo, pozice, novy_znak):
+    """V daném slově zamění znak na dané pozici za daný nový znak."""
+    zacatek = slovo[:pozice]
+    konec = slovo[pozice + 1:]
+    nove_slovo = zacatek + novy_znak + konec
+    return nove_slovo
+
+print(zamen('kočka', 1, 'a'))
+print(zamen('kačka', 2, 'p'))
+

Jak to funguje?

+

Funkce se definuje příkazem def, za nějž napíšeš jméno funkce, +pak do závorky seznam parametrů, které funkce bere, a pak dvojtečku.

+

Potom následuje odsazené tělo funkce – příkazy, které funkce provádí.

+

Tělo může začít dokumentačním řetězcem (angl. docstring), který popisuje, +co funkce dělá. +To může být jakýkoli řetězec, ale tradičně se uvozuje třemi uvozovkami +(i v případě, že je jen jednořádkový).

+

Příkazem return pak můžeš z funkce vrátit nějakou hodnotu.

+

Při volání funkce se hodnoty, se kterými funkci +zavoláš, přiřadí jednotlivým parametrům. +Takže když zavoláš třeba zamen('kočka', 1, 'a'), +můžeš si představit, že se provede toto:

+
# Nastavení proměnných podle zadaných argumentů
+slovo = 'kočka'
+pozice = 1
+novy_znak = 'a'
+
+# Samotné tělo funkce
+zacatek = slovo[:pozice]
+konec = slovo[pozice + 1:]
+nove_slovo = zacatek + novy_znak + konec
+return nove_slovo
+

Už víš, že volání zamen('kočka', 1, 'a') je výraz. +Aby ho Python vyhodnotil, udělá celý postup výše a jako hodnotu výrazu dosadí +návratovou hodnotu – tedy to, co následuje po return.

+

Tělo funkce může mít více příkazů – včetně podmínek, cyklů a podobně. +Následující procedura třeba vypíše skóre daného hráče a k tomu hlášku:

+
def napis_hlasku(nazev, skore):
+    """Popíše skóre. Název má být přivlastňovací přídavné jméno."""
+
+    print(nazev, 'skóre je', skore)
+    if skore > 1000:
+        print('Světový rekord!')
+    elif skore > 100:
+        print('Skvělé!')
+    elif skore > 10:
+        print('Ucházející.')
+    elif skore > 1:
+        print('Aspoň něco')
+    else:
+        print('Snad příště.')
+
+napis_hlasku('Tvoje', 256)
+napis_hlasku('Protivníkovo', 5)
+

Cvičení +# +

+

Zkus napsat funkci, která vrátí obsah obdélníka daných rozměrů. +Příslušný vzoreček je S = a×b, +kde a a b jsou délky stran.

+

Funkci zavolej a výsledek vypiš.

+
+

Řešení

+ + +
+ +

Vracení ukončuje funkci +# +

+

Speciální příkaz return, který jde použít jenom ve funkcích, vrátí danou +návratovou hodnotu ven z funkce a zároveň ukončí provádění funkce.

+

Chová se tedy trochu jako break, jen místo cyklu opouští celou funkci.

+

Podobně jako break se dá použít v případech, kdy potřebuješ od uživatele +dostat odpověď – a opakuješ dotaz tak dlouho, dokud požadovanou odpověď +nedostaneš. +Třeba, chceš-li odpověď „ano“ nebo „ne“:

+
    +
  • Takhle se zjišťuje odpověď ano (Pravda) nebo ne (Nepravda) na danou otázku:
      +
    • Pořád dokola:
        +
      • Zeptej se na otázku; zapamatuj si odpověď.
      • +
      • Je-li odpověď „ano“:
          +
        • Výsledek je Pravda. Hotovo; dál nepokračuj.
        • +
        +
      • +
      • Jinak, je-li odpověď „ne“:
          +
        • Výsledek je Nepravda. Hotovo; dál nepokračuj.
        • +
        +
      • +
      • Pouč uživatele, ať odpoví „ano“ nebo „ne“. +
        (a zkus to znovu – viz „Pořád dokola“)
      • +
      +
    • +
    +
  • +
+
def ano_nebo_ne(otazka):
+     """Vrátí True nebo False podle odpovědi uživatele"""
+    while True:
+        odpoved = input(otazka)
+        if odpoved == 'ano':
+            return True
+        elif odpoved == 'ne':
+            return False
+
+        print('Nerozumím! Odpověz "ano" nebo "ne".')
+
+# Příklad použití
+if ano_nebo_ne('Chceš si zahrát hru? '):
+    print('OK! Ale napřed si ji musíš naprogramovat.')
+else:
+    print('Škoda.')
+

Stejně jako if nebo break je return příkaz, ne funkce. +Kolem „své“ hodnoty nepotřebuje závorky.

+
\ No newline at end of file diff --git a/lessons/deployment/index.html b/lessons/deployment/index.html new file mode 100644 index 00000000..4ebf0225 --- /dev/null +++ b/lessons/deployment/index.html @@ -0,0 +1,27 @@ +

Deployment webových aplikací +# +

+

Aplikace napsaná v Pythonu běží na našem počítači, ale jak ji dostat do Internetu? +Existují různé možnosti, jednou z nich je nasadit ji do cloudu.

+

Nemáte ještě webovou aplikaci? Můžete vyzkoušet framework +Flask.

+

WSGI +# +

+

Nasazování webových aplikací v Pythnu se opírá o WSGI, +což je standardní pythonní rozhraní pro komunikaci +mezi webovou aplikací a webovým serverem definované v PEPu 333.

+

Naprostá většina webových frameworků v Pythonu toto rozhraní implementuje přímo, +případně k tomuto účelu obsahuje wrapper.

+

Je tedy jedno, jestli používáte Flask, Pyramid, Django, Bottle nebo Falcon, +vždy vaší aplikaci představuje application objekt, který se navenek chová +stejně. Webové frameworky implementují aplikační část WSGI.

+

Stejně tak existují webové servery, které implementují serverovou část WSGI, +například Gunicorn nebo mod_wsgi pro httpd (Apache). Tyto servery umí +pracovat s application objektem a nezajímá je, v jakém frameworku je aplikace +napsaná.

+

Většině cloudových providerům stačí nějakým způsobem application objekt předat +a o zbytek se postarají za vás. Jedním z takových providerů je i +PythonAnywhere.

+

Deployment webových aplikací na PythonAnywhere je popsaný v lekci +PythonAnywhere.

\ No newline at end of file diff --git a/lessons/deployment/pythonanywhere.html b/lessons/deployment/pythonanywhere.html new file mode 100644 index 00000000..4cbc292c --- /dev/null +++ b/lessons/deployment/pythonanywhere.html @@ -0,0 +1,83 @@ +

Deployment webových aplikací na PythonAnywhere +# +

+

PythonAnywhere je pro limitované použití zdarma.

+

K posílání kódu na produkční prostředí budeme používat Git. +Nejprve proto uložte celý projekt do Gitu a nahrajte na GitHub.

+

Potom se zaregistrujte na +www.pythonanywhere.com a vyberte +Beginner Account. +Po přihlášení se ukáže záložka Consoles, kde vytvořte "Bash" konzoli. +V té vytvořte a aktivujte virtuální prostředí a nainstalujte Flask (plus +případně další závislosti nebo jiný webový framework).

+

PythonAnywhere používá specificky nastavený Linux, +tak je ve webové konzoli potřeba použít jiný příkaz +na vytvoření virtuální prostředí, než jste z toho kurzu zvyklí. +Napište příkazy takto (bez úvodního $):

+
$ virtualenv --python=python3.7 __venv__
+$ . __venv__/bin/activate
+$ python -m pip install flask
+

Pokud máte na PythonAnywhere starší účet, možná tam Python 3.7 nenajdete. +Můžete použít Python 3.6, nemělo by to vadit, protože tento návod je +koncipován tak, aby s touto verzí také fungoval. +Případně můžete zažádat o aktualizaci systémové +image.

+

Následně naklonujte na PythonAnywhere váš kód. +S veřejným repozitářem je to jednodušší – stačí ho naklonovat „anonymně” +(git clone https://github.com/<github-username>/<github-repo>). +Pokud ale používáme privátní repozitář, bude potřeba si vygenerovat SSH klíč:

+
$ ssh-keygen  # (zeptá se na hesla ke klíči)
+$ cat ~/.ssh/id_rsa.pub
+

Obsah souboru ~/.ssh/id_rsa.pub je pak potřeba přidat na GitHub v osobním +nastavení v sekci "SSH and GPG Keys". +Pak můžete klonovat přes SSH:

+
$ git clone git@github.com:<github-username>/<github-repo>.git
+

Zbývá nastavit, aby PythonAnywhere tento kód spustil jako webovou aplikaci.

+

Přejděte na stránkách PythonAnywhere do Dashboard do záložky Web, +a vytvořte novou aplikaci. +V nastavení zvolte Manual Configuration a Python 3.7. +(Volby jiné než Manual Configuration automaticky vytvoří kostru aplikace. +Vy ale už aplikaci máte hotovou, takže je nepotřebujete.)

+

V konfiguraci vzniklé webové aplikace je potřeba nastavit Virtualenv +na cestu k virtuálnímu prostředí (/home/<uživatelské-jméno>/venv), +a obsah WSGI Configuration File přepsat. +To jde buď kliknutím na odkaz v konfiguraci (otevře se webový editor) +nebo zpět v bashové konzoli pomocí editoru jako vi nebo nano.

+

Nový obsah souboru by měl být:

+
import sys
+path = '/home/<uživatelské-jméno>/<jméno-adresáře>'
+if path not in sys.path:
+    sys.path.append(path)
+
+from <jméno-souboru> import app as application
+

(Za <uživatelské-jméno>, +<jméno-adresáře> a +<jméno-souboru> +je samozřejmě potřeba doplnit +vaše údaje. Jméno souboru je zde bez přípony .py.)

+

Nakonec restartujte aplikaci velkým zeleným tlačítkem na záložce Web +a na adrese <uživatelské-jméno>.pythonanywhere.com +si ji můžete prohlédnout.

+

Deployment soukromých údajů +# +

+

Protože vaše hesla, tajné klíče apod. nejsou v repozitáři, je nutné je předat +aplikaci zvlášť. +Konfigurační i jiné soubory jde nahrát v záložce Files nebo opět vytvořit +a editovat ve webové konzoli.

+

Pokud vaše aplikace vyžaduje nastavení nějakých proměnných prostředí +(například s cestou ke konfiguračnímu souboru nebo přímo s nějakou konfigurací), +můžete tak učinit přímo z WSGI Configuration File. +Buďto „nízkoúrovňově“ (os.environ) nebo více sofistikovaně například pomocí modulu dotenv, +což ostatně doporučují i v dokumentaci.

+

Doporučujeme pro tyto potřeby stejně raději nepoužívat API klíče +k vlastním účtům, raději si vyrobte nějaké účty pouze pro tento účel. +GitHub povoluje všem vytvořit si jeden účet pro automatické operace, ale +takový účet musí mít napsané v popisu, že je robot.

+

Aktualizace +# +

+

Když nahrajeme nový kód na GitHub, je vždy potřeba provést na PythonAnywhere +v konzoli git pull a pak v záložce Web aplikaci restartovat.

+

Placená varianta PythonAnywhere má API a tento proces jde zautomatizovat.

+

Ve verzi zadarmo to není tak pohodlné.

\ No newline at end of file diff --git a/lessons/dict-with-list-values/index.html b/lessons/dict-with-list-values/index.html new file mode 100644 index 00000000..4118e383 --- /dev/null +++ b/lessons/dict-with-list-values/index.html @@ -0,0 +1,54 @@ +

Více hodnot v jednom záznamu slovníku +# +

+

Ke každému klíči může patřit jen jedna hodnota. +Jak bys zařídil/a, aby hodnot bylo víc?

+

Řekněme, že bys chtěl/a do slovníku uložit tyto telefonní kontakty:

+
    +
  • Katka:
      +
    • 4925219
    • +
    +
  • +
  • Jirka:
      +
    • 7477058
    • +
    • 3251156
    • +
    +
  • +
  • Verča:
      +
    • 1019103
    • +
    +
  • +
+

Jak na to? +Jednoduchý slovník použít nemůžeš. Pozor na následující zápis!

+
kontakty = {
+    'Katka': '4925219',
+    'Jirka': '7477058',
+    'Jirka': '3251156',
+    'Verča': '1019103',
+}
+

Python v tomto případě jednotlivé záznamy uloží postupně, jako kdybys napsal/a:

+
kontakty = {}
+kontakty['Katka'] = '4925219'
+kontakty['Jirka'] = '7477058'
+kontakty['Jirka'] = '3251156'
+kontakty['Verča'] = '1019103'
+

A ve výsledku bude mít 'Jirka' uložené jen jedno číslo. +Python tohle nenahlásí jako chybu, ačkoli to často není to, co chceš.

+

Protože ve slovníku může být každému klíči přiřazena jen jedna hodnota, +bude potřeba více hodnot směstnat do jedné. +Každému kontaktu můžeš přiřadit seznam čísel:

+
kontakty = {
+    'Katka': ['4925219'],
+    'Jirka': ['7477058', '3251156'],
+    'Verča': ['1019103'],
+}
+

Výraz jako kontakty['Katka'] pak označuje seznam. +Pokud se Katka přestěhuje do zahraničí a pořídí si nové číslo, +můžeš napsat:

+
kontakty['Katka'].append('+897 3788509')
+print(len(kontakty['Katka']))
+print(kontakty['Katka'][-1])
+del kontakty['Katka'][0]
+print(kontakty['Katka'])
+
\ No newline at end of file diff --git a/lessons/dict/index.html b/lessons/dict/index.html new file mode 100644 index 00000000..93bb5fdb --- /dev/null +++ b/lessons/dict/index.html @@ -0,0 +1,195 @@ +

Slovníky +# +

+

Další základní datový typ, který si představíme – +po číslech, řetězcích, seznamech a n-ticích – +je slovník (angl. dictionary, dict).

+

Představ si překladový slovník, třeba tenhle česko-anglický:

+
    +
  • Jablko: Apple
  • +
  • Knoflík: Button
  • +
  • Myš: Mouse
  • +
+

Slovník v Pythonu obsahuje záznamy. Každý záznam přiřazuje +nějakému klíči nějakou hodnotu. +V našem příkladu je klíči Jablko přiřazena hodnota Apple, +klíči Knoflík náleží hodnota Button +a klíč Myš ukazuje na Mouse.

+

V Pythonu by se takový slovník napsal následovně:

+
>>> slovnik = {'Jablko': 'Apple', 'Knoflík': 'Button', 'Myš': 'Mouse'}
+>>> slovnik
+{'Jablko': 'Apple', 'Knoflík': 'Button', 'Myš': 'Mouse'}
+

Pozor na všechny ty symboly! +V tomhle slovníku jsou klíče i hodnoty řetězce, takže jsou v uvozovkách. +Každý klíč je od své hodnoty oddělený dvojtečkou; +jednotlivé dvojice jsou od sebe oddělené čárkou. +A celý slovník je uzavřený ve složených závorkách.

+

Když budeš chtít v takovém slovníku něco najít, potřebuješ vědět co hledat. +Konkrétně potřebuješ klíč. +Ten dáš do hranatých závorek:

+
>>> slovnik['Jablko']
+'Apple'
+

Je to podobné jako u seznamů, jen v hranatých závorkách není index +(pořadové číslo prvku) nebo rozmezí s dvojtečkou, ale právě klíč.

+

Naopak to nejde – slovník neumožňuje podle hodnoty přímo zjistit klíč. +Na překlad z angličtiny do češtiny bys potřeboval/a druhý slovník.

+

Měnění slovníků +# +

+

Co se stane, když klíč ve slovníku není?

+
>>> slovnik['Pes']
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+KeyError: 'Pes'
+

Python si postěžuje na KeyError – chybu klíče.

+

Podobně jako seznamy se ale slovníky dají měnit. +Nový záznam vytvoříš takhle:

+
>>> slovnik['Pes'] = 'Dog'
+>>> slovnik
+{'Jablko': 'Apple', 'Knoflík': 'Button', 'Myš': 'Mouse', 'Pes': 'Dog'}
+

Na rozdíl od překladového slovníku nemusí být Pythonní slovník seřazený +podle abecedy. +Není to potřeba, počítač umí rychle vyhledávat i bez seřazení.

+

Kdybys potřeboval/a změnit už existující záznam, použij stejný příkaz. +K jednomu klíči může být přiřazena jen jedna hodnota.

+
>>> slovnik['Pes'] = 'Power strip'
+>>> slovnik
+{'Jablko': 'Apple', 'Knoflík': 'Button', 'Myš': 'Mouse', 'Pes': 'Power strip'}
+

Chceš-li ze slovníku nějaký záznam smazat, dělá se to podobně jako +u seznamů – příkazem del:

+
>>> del slovnik['Pes']
+>>> slovnik
+{'Jablko': 'Apple', 'Knoflík': 'Button', 'Myš': 'Mouse'}
+

Když budeš chtít zjistit, kolik je ve slovníku záznamů, +zeptáš se podobně jako na počet znaků řetězce nebo prvků seznamu. +Použiješ funkci len().

+
>>> len(slovnik)
+3
+

A pomocí in můžeš zjistit, jestli slovník obsahuje daný klíč. +Funguje to opravdu jen pro klíče, ne pro přiřazené hodnoty:

+
>>> 'Myš' in slovnik
+True
+>>> 'Mouse' in slovnik
+False
+

Iterace +# +

+

Když slovník projdeš cyklem for, dostaneš klíče jednotlivých záznamů:

+
>>> for klic in slovnik:
+...     print(klic)
+Jablko
+Knoflík
+Myš
+

Kdybys chtěl/a projít místo klíčů hodnoty, použij metodu values, +která vrací iterátor hodnot:

+
>>> for hodnota in slovnik.values():
+...     print(hodnota)
+Apple
+Button
+Mouse
+

Většinou ale potřebuješ jak klíče tak hodnoty. +K tomu mají slovníky metodu items, která vrací iterátor dvojic. +Často využiješ možnost každou dvojici přímo rozbalit v cyklu for, +jako se to dělá se zip nebo enumerate:

+
>>> for klic, hodnota in slovnik.items():
+...     print('{}: {}'.format(klic, hodnota))
+Jablko: Apple
+Knoflík: Button
+Myš: Mouse
+

V průběhu iterace (tedy v rámci for cyklu) nesmíš +do slovníku přidávat záznamy, ani záznamy odebírat:

+
>>> for klic in slovnik:
+...     del slovnik[klic]
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+RuntimeError: dictionary changed size during iteration
+

Hodnoty u už existujících klíčů ale měnit můžeš.

+

Jak udělat slovník +# +

+

Slovník se dá vytvořit několika způsoby. +První, pomocí složených závorek, jsi už viděl/a. +Další způsob využívá funkci dict. +Ta, ve stylu str, int či list, převede cokoli co jde na slovník.

+

Slovník je ovšem dost specifická struktura – +čísla ani většina seznamů na něj převést nejdou. +Můžeš ale na slovník převést jiný slovník. +Nový slovník pak žije svým vlastním životem; +můžeš ho měnit nezávisle na tom původním.

+

Druhá věc, která jde převést na slovník, je +sekvence dvojic klíč/hodnota – ať už seznam:

+
>>> data = [(1, 'jedna'), (2, 'dva'), (3, 'tři')]
+>>> nazvy_cisel = dict(data)
+>>> nazvy_cisel
+{1: 'jedna', 2: 'dva', 3: 'tři'}
+

nebo jiný iterovatelný objekt:

+
>>> data = enumerate(['nula', 'jedna', 'dva'])
+>>> nazvy_cisel = dict(data)
+>>> nazvy_cisel
+{0: 'nula', 1: 'jedna', 2: 'dva'}
+

A to je vše, co se na slovník dá převést.

+

Jako bonus umí funkce dict ještě +brát pojmenované argumenty. +Každé jméno argumentu převede na řetězec, +použije ho jako klíč, a přiřadí danou hodnotu:

+
popisy_funkci = dict(len='délka', str='řetězec', dict='slovník')
+print(popisy_funkci['len'])
+

Pozor na to, že v tomhle případě musí být klíče +pythonní „jména“ – musí být použitelné jako jména proměnných. +Například takhle nejde zadat jako klíč řetězec +"def" nebo "propan-butan".

+

Zaplň prázdný slovník +# +

+

Nejobecnější způsob vytváření slovníků je podobný tomu, co znáš u seznamů: +vytvoř prázdný slovník a postupně do něj přidávej záznamy, jeden za druhým.

+

Řekněme, že máš slovník, který přiřazuje ovoci jeho barvu:

+
barvy = {
+    'hruška': 'zelená',
+    'jablko': 'červená',
+    'meloun': 'zelená',
+    'švestka': 'modrá',
+    'ředkvička': 'červená',
+    'zelí': 'zelená',
+    'mrkev': 'červená',
+}
+

Následující kód vytvoří slovník se změněnými barvami:

+
barvy_po_tydnu = {}
+for ovoce, barva in barvy.items():
+    barvy_po_tydnu[ovoce] = 'černo-hnědo-' + barva
+
+print(barvy_po_tydnu['jablko'])
+

Typy klíčů a hodnot +# +

+

Do slovníku můžeš uložit jakoukoli hodnotu: řetězce, seznamy, nebo čísla.

+
uzivatel = {'jméno': 'Amálka', 'velikost nohy': 36, 'oblíbená čísla': [5, 27]}
+

Jako klíč jde ovšem použít jen hodnoty, podle kterých pak Python umí záznam +rychle najít. +A to nejsou všechny. Řetězce a čísla použít jdou:

+
jmena_cisel = {2: 'dva', 3: 'tři'}
+

Ale seznamy nebo jiné slovníky ne. +Typy, které se dají použít jako klíč ve slovníku, se technicky označují jako +„hashovatelné“ (angl. hashable). +Tento termín se objevuje v chybových hláškách:

+
>>> jmena_seznamu = {[1, 2, 3]: 'čísla', ['a', 'b', 'c']: 'řetězce'}
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+TypeError: unhashable type: 'list'
+

N-tice jsou hashovatelné, pokud obsahují jen hashovatelné hodnoty:

+
>>> figurky = {('c', 1): 'bílý střelec', ('e', 8): 'černý král'}
+>>> figurky['c', 1]
+'bílý střelec'
+
>>> jmena = {([1, 2, 3], [3, 4, 5]): 'dvojice seznamů'}
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+TypeError: unhashable type: 'list'
+

A to je zatím ke slovníkům vše +# +

+

Chceš-li mít všechny triky, které slovníky umí, +pěkně pohromadě, můžeš si stáhnout +Slovníkový tahák.

+

Kompletní popis slovníků najdeš +v dokumentaci +Pythonu.

\ No newline at end of file diff --git a/lessons/distribution/index.html b/lessons/distribution/index.html new file mode 100644 index 00000000..3ef4ada4 --- /dev/null +++ b/lessons/distribution/index.html @@ -0,0 +1,437 @@ +

Moduly +# +

+

Zatím jsme tvořili programy v Pythonu tak nějak na divoko, tedy v jednom nebo +více souborech bez nějakého zvláštního řádu. V této lekci se podíváme na +to, jak tvořit redistribuovatelné moduly a balíčky, které jdou nahrát na PyPI +(veřejný seznam balíčků pro Python) a instalovat pomocí nástroje pip.

+

Za příklad si vezmeme kód Ondřeje Caletky, který umožňuje určit české svátky +v zadaném roce. Jako příklad je ideální, protože obsahuje jak funkce, které +můžeme volat z Pythonu, tak lze volat z příkazové řádky.

+ +

Volání z příkazové řádky, pomocí příkazu python isholiday.py nebo +python -m isholiday, zajišťuje blok if __name__ == '__main__':. +Toto je rychlý způsob, jak napsat modul, který jde jak importovat, tak spustit. +Když nějaký modul importujeme, má v proměnné __name__ k dispozici své jméno. +„Hlavní” modul ale není importován a jeho jméno není vždy k dispozici +(např. v cat isholiday.py | python). +Python proto __name__ „hlavního” modulu nastavuje na '__main__', +čehož se často využívá.

+

Později se podíváme na elegantnější způsob jak to zařídit; teď se vraťme +zpět k balíčkování.

+

Slovníček pojmů +# +

+

Než se pustíme do samotného výkladu, zavedeme některé pojmy tak, +aby mezi nimi nedošlo v textu záměně. +Anglické pojmy v závorce jsou převzaty z oficiálního glosáře.

+
    +
  • (importovatelný) modul (ModuleImport Package) je cokoliv, +co se dá importovat z Pythonu, v tomto textu tedy především Python soubor nebo adresář s nimi;
  • +
  • balíček (Distribution Package) je instalovatelný archiv obsahují +importovatelné moduly pro Python a další potřebné soubory, může být i rozbalený;
  • +
  • zdrojový balíček (Source Distribution, sdsit) je varianta zabaleného balíčku ve zdrojové formě;
  • +
  • binární balíček (Binary Distribution, bdsit) je varianta zabaleného balíčku v nezdrojové (např. zkompilované) formě;
  • +
  • projekt (Project) je knihovna, framework, skript, plugin, aplikace apod. (či jejich kombinace), které balíme do balíčků.
  • +
+

setup.py +# +

+

Základním stavebním kamenem Python balíčku je soubor setup.py, který +obsahuje všechna potřebná metadata ve volání funkce setup() z modulu +setuptools.

+

Pojďme vytvořit jeho minimální variantu:

+
from setuptools import setup
+
+
+setup(
+    name='isholiday',
+    version='0.1',
+    description='Finds Czech holiday for given year',
+    author='Ondřej Caletka',
+    author_email='ondrej@caletka.cz',
+    license='Public Domain',
+    url='https://gist.github.com/oskar456/e91ef3ff77476b0dbc4ac19875d0555e',
+    py_modules=['isholiday'],
+)
+

Všimněte si, že jsme balíček pojmenovali stejně jako soubor se zdrojovým kódem +(tedy stejně jako modul). +Je to dobrá konvence, ale není to technicky nutné.

+

Balíček můžeme zkusit nainstalovat do virtuálního prostředí:

+
$ python3.7 -m venv __venv__     # (nebo jinak -- podle vašeho OS)
+$ . __venv__/bin/activate        # (nebo jinak -- podle vašeho OS)
+(__venv__)$ python setup.py install
+...
+(__venv__)$ python
+>>> import isholiday
+>>> 
+(__venv__)$ python -m pip freeze
+isholiday==0.1
+

Souboru setup.py rozumí i nástroj pip, takže můžete použít ten:

+
(__venv__)$ python -m pip install .
+

Mezi výše uvedenými příkazy existují rozdíly, ale pro základní použití se výsledek neliší.

+

Alternativně můžete použít příkaz develop (nebo pip install --editable), +který balíček nainstaluje tak, že změny v souborech se projeví rovnou +(není třeba po každé změněně instalovat znovu).

+

Přes setup.py můžeme dělat i jiné věci, než jen instalovat, například vytvořit archiv, zdrojový balíček:

+
(__venv__)$ python setup.py sdist
+...
+warning: sdist: standard file not found: should have one of README, README.rst, README.txt
+...
+

Extra soubory do zdrojového balíčku +# +

+

Jak vidíte, setuptools si stěžuje, že náš projekt nemá README – soubor, +do kterého se tradičně píšou základní informace o projektu. +Můžeme jej vytvořit a uložit jako README přímo v kořenovém adresáři projektu, +tedy tam, kde byste jej nejspíš čekali.

+
Czech public holiday checker...
+

Poté spustíme setup.py sdist znovu:

+
(__venv__)$ python setup.py sdist
+

V adresáři dist najdete archiv, jeho obsah můžete zkontrolovat. Měl by tam +být i soubor README.

+

Skvělé, pojďme vytvořit i další speciální soubor, LICENSE, který bude +obsahovat text licence, v tomto případě Public Domain. +Obsah najdete třeba na CC0.

+

Pokud ale se souborem LICENSE vytvoříte zdrojový balíček, soubor v archivu +nebude. Je to proto, že se standardně do archivu přidávají jen některé soubory. +Další soubory lze přidat pomocí souboru MANIFEST.in, dle dokumentace.

+

V našem případě bude MANIFEST.in vypadat takto:

+
include LICENSE
+

Při dalším spuštění už setup.py přidá i soubor LICENSE. +To můžete zkontrolovat i ve výsledném archivu.

+
(__venv__)$ python setup.py sdist
+...
+hard linking LICENSE -> isholiday-0.1
+hard linking MANIFEST.in -> isholiday-0.1
+hard linking README -> isholiday-0.1
+...
+

Hotový balíček pak můžete nainstalovat pomocí nástroje pip. +Doporučuji to dělat v jiném virtuálním prostředí – v aktuálním už ho máte +nainstalovaný.

+
# v jiné konzoli, v jiném adresáři
+$ python3 -m venv __venv2__
+$ . __venv2__/bin/activate
+(__venv2__)$ python -m pip install cesta/k/projektu/dist/isholiday-0.1.tar.gz
+Processing cesta/k/projektu/dist/isholiday-0.1.tar.gz
+Installing collected packages: isholiday
+  Running setup.py install for isholiday ... done
+Successfully installed isholiday-0.1
+

Více argumentů pro setup() +# +

+

Na chvíli se vrátíme k volání funkce setup() a přidáme co nejvíc dalších +položek. +Jejich vysvětlení najdete v dokumentaci.

+
from setuptools import setup
+
+
+with open('README') as f:
+    long_description = ''.join(f.readlines())
+
+
+setup(
+    name='isholiday',
+    version='0.1',
+    description='Finds Czech holiday for given year',
+    long_description=long_description,
+    author='Ondřej Caletka',
+    author_email='ondrej@caletka.cz',
+    keywords='holiday,dates',
+    license='Public Domain',
+    url='https://gist.github.com/oskar456/e91ef3ff77476b0dbc4ac19875d0555e',
+    py_modules=['isholiday'],
+    classifiers=[
+        'Intended Audience :: Developers',
+        'License :: Public Domain',
+        'Operating System :: POSIX :: Linux',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: Implementation :: CPython',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.6',
+        'Programming Language :: Python :: 3.7',
+        'Topic :: Software Development :: Libraries',
+        ],
+    zip_safe=False,
+)
+

Všimněte si několika věcí. V první řadě v long_description vidíte, že jsme +pořád ještě v Pythonu a můžeme si ušetřit duplikaci nějakých informací pomocí +malého kousku kódu. Dalším zajímavým argumentem je classifiers. Jsou to +v podstatě takové tagy nebo strukturované informace o balíčku. +Zásadně si je nevymýšlíme sami, ale hledáme je v +seznamu. +Tyto informace budou později vidět na PyPI a +půjde podle nich hledat.

+

Argument zip_safe=False zajistí, že se moduly z balíčku nainstalují do adresáře. +Setuptools totiž mají nepříjemný zlozvyk instalovat moduly jako zip, +což komplikuje práci s datovými soubory (např. templates pro Flask). +Je proto lepší zip_safe=False uvést.

+

Více souborů s Python kódem +# +

+

Doteď jsme vytvářeli balíček jen s modulem ve formě jednoho zdrojového souboru isholiday.py. +Co ale dělat, pokud je náš projekt větší a obsahuje souborů více? +Teoreticky je možné je přidat všechny do py_modules, ale není to dobrý nápad.

+

Proč to vlastně není dobrý nápad? Jednotlivé moduly ze všech nainstalovaných +balíčků by byly rozesety bez ladu a skladu mezi ostatními. +Mohl by snadno nastat konflikt v názvech, například pokud by více balíčků +mělo modul utils. +Slušně vychovaný Pythonista dá do každého balíčku právě jeden hlavní modul, +pojmenovaný stejně jako balíček a všechny ostatní moduly zanoří do něj.

+

Raději uděláme modul ve formě složky. V našem případě soubor +isholiday.py zatím přesuneme do isholiday/__init__.py:

+
(__venv__)$ tree
+.
+├── isholiday
+│   └── __init__.py
+├── LICENSE
+├── MANIFEST.in
+├── README
+└── setup.py
+
+1 directory, 5 files
+

Soubor __init__.py jednak značí, že adresář isholiday je importovatelný modul, +a také obsahuje kód, který se spustí při importu modulu isholiday.

+

Musíme ještě mírně upravit setup.py – místo py_modules použijeme packages:

+
diff --git a/setup.py b/setup.py
+index 3a69792..6b453ab 100644
+--- a/setup.py
++++ b/setup.py
+@@ -11,7 +11,7 @@ setup(
+     keywords='holiday,dates',
+     license='Public Domain',
+     url='https://gist.github.com/oskar456/e91ef3ff77476b0dbc4ac19875d0555e',
+-    py_modules=['isholiday'],
++    packages=['isholiday'],
+     classifiers=[
+         'Intended Audience :: Developers',
+         'License :: Public Domain',
+

Případně, což je ještě lepší, můžeme použít find_packages():

+
from setuptools import setup, find_packages
+
+setup(
+    ...
+    packages=find_packages(),
+    ...
+)
+

A jaký je tedy vlastně rozdíl mezi py_modules a packages? +Zjednodušeně: Ten první je na moduly sestávající z jednoho souboru, ten druhý na moduly v adresáři.

+

Momentálně máme všechen kód přímo v __init__.py, což sice funguje, +ale ideální to není. Dobré je mít kód v samostatných souborech a v __init__.py +pouze importovat veřejné rozhraní, tedy to, co budou z vašeho modulu importovat +jeho uživatelé.

+

V souboru __init__.py by tak prakticky žádný kód kromě importů být neměl. +Přesuňte tedy obsah __init__.py do holidays.py a +do __init__.py místo toho napište:

+
from .holidays import getholidays, isholiday
+
+__all__ = ['getholidays', 'isholiday']
+

Tečka v příkazu import není chyba: je to zkratka pro aktuální modul. +Můžeme psát i from isholiday.holidays import ..., +což ale trochu ztěžuje případné přejmenování modulu.

+

Ono __all__ pak explicitně definuje rozhraní modulu. Například s původním +modulem šlo provést from isholiday import datetime, ale asi by nikdo +nečekal, že tahle možnost bude nutně zachována i v příštích verzích knihovny. +Seznamem __all__ dáte najevo, že tyhle funkce nejsou jen „náhodné importy“, +a zároveň tím zamezíte různým varováním o +importovaném ale nevyužitém modulu, které může hlásit vaše IDE nebo linter.

+

Python samotný pak __all__ používá jako seznam proměnných importovaných +přes from isholiday import *. Tento způsob importu nevidíme rádi, +protože znepřehledňuje kód, to ale neznamená, že to musíme uživatelům +naší knihovny znepříjemňovat (např. pro interaktivní režim).

+

Spouštění modulu +# +

+

Pokusíme-li se teď program spustit pomocí python -m isholiday, +narazíme na problém: na rozdíl od souboru se složka s kódem takto spustit nedá:

+
$ python -m isholiday
+python: No module named isholiday.__main__; 'isholiday' is a package and cannot be directly executed
+

Namísto spuštění souboru (typicky s blokem if __name__ == '__main__':) totiž +Python v tomto případě hledá soubor pojmenovaný __main__.py a spustí ten.

+

Soubor __main__.py není určený k tomu, aby se z něho importovalo, proto +by měl obsahovat co nejméně kódu – ideálně jen volání funkce, která je +definovaná jinde. Vytvořte proto __main__.py s následujícím obsahem:

+
from .holidays import main
+
+main()
+

a v holidays.py zaměňte if __name__ == '__main__': za def main():.

+

Modul teď bude možné (opět) spustit pomocí python -m isholiday. +Bude to fungovat i tehdy, když vytvoříte balíček (python setup.py sdist) +a nainstalujete ho v jiném virtuálním prostředí.

+

Programy pro příkazovou řádku +# +

+

Pokud chcete, aby váš modul umožňoval spouštění přímo z příkazové řádky, +bez python -m, měli byste použít entrypoints. +K tomu je potřeba přidat do volání setup v setup.py příslušný argument:

+
setup(
+    ...
+    entry_points={
+        'console_scripts': [
+            'isholiday_demo = isholiday.holidays:main',
+        ],
+    },
+)
+

isholiday_demo je jméno entrypointu, tedy příkazu pro příkazovou řádku. +isholiday.holidays:main je pak cesta k funkci ve tvaru modul:funkce; +funkce může být v modulu definovaná nebo importovaná.

+

Skript bude možné použít, je-li aktivní prostředí, kde je nainstalován, jen +zadáním jména entrypointu:

+
(__venv__)$ python setup.py sdist
+
# v jiné konzoli, v jiném virtuálním prostředí
+(__venv2__)$ python -m pip install --upgrade cesta/k/projektu/dist/isholiday-0.1.tar.gz
+(__venv2__)$ isholiday_demo
+...
+Mon Mar 28 00:00:00 2016 True
+Tue Mar 28 00:00:00 2017 False
+Fri Apr 14 00:00:00 2017 True
+

Specifikace závislostí +# +

+

Balíčky na PyPI mohou záviset na dalších balíčkách. V případě isholiday to +potřeba není, ale v úlohách z minulých cvičení ano.

+

Existuje několik úrovní závislostí, ve většině případů si +vystačíte s argumentem install_requires. +Balíček, který závisí na balíčkách Flask (jakékoli verze) a +click (verze 6 a vyšší) by v setup.py měl mít:

+
setup(
+    ...
+    install_requires=['Flask', 'click>=6'],
+)
+

Soubor requirements.txt +# +

+

Kromě závislostí v setup.py se u pythonních projektů často setkáme se souborem +requirements.txt, který obsahuje přesné verze všech závislostí, včetně +tranzitivních – t.j. závisí-li náš balíček na Flask a Flask na Jinja2, +najdeme v requirements.txt mimo jiné například řádky:

+
Flask==0.11.1
+Jinja2==2.8
+

Tento soubor se používá, když je potřeba přesně replikovat prostředí, kde +program běží, například mezi testovacím strojem a produkčním nasazením +webové aplikace. +Tento soubor se dá vygenerovat z aktuálního prostředí zadáním +python -m pip freeze > requirements.txt a balíčky v něm se dají nainstalovat +pomocí python -m pip install -r requirements.txt. +My ho používat nebudeme, vystačíme si s volnější specifikací závislostí +v setup.py.

+

Nahrání na PyPI +# +

+

Balíček jde zaregistrovat a nahrát na PyPI. Původně k tomu sloužily příkazy +setup.py register a upload, ale tyto příkazy používaly HTTP, což není +bezpečné. Prototo je lepší použít program twine (instalovatelný přes pip), +který používá HTTPS.

+

Budete si potřebovat zařídit +účet na PyPI, +účet na testovací PyPI +a vytvořit konfigurační soubor ~/.pypirc:

+
[distutils]
+index-servers=
+    pypi
+    testpypi
+
+[pypi]
+username = <your user name goes here>
+password = <your password goes here>
+
+[testpypi]
+repository = https://test.pypi.org/legacy/
+username = <your user name goes here>
+password = <your password goes here>
+

Hesla můžete vynechat, pokud je budete chtít pokaždé zadávat.

+

Používáte-li Windows, je potřeba nastavit proměnnou prostředí HOME na adresář +se souborem .pypirc, např:

+
> set HOME=C:\cesta\k\nastaveni
+

Registrace projektu a nahrání na testovací PyPI se provádí pomocí příkazu +upload: ten projekt zaregistrueje (pokud to jde) a nahraje samotný balíček:

+
(__venv__)$ twine upload -r testpypi dist/isholiday-0.1.tar.gz
+Uploading distributions to https://test.pypi.org/legacy/
+Uploading isholiday-0.1.tar.gz
+[================================] 8379/8379 - 00:00:02
+

První nahrání se zdaří, jen pokud jméno projektu již není zabrané. +Další nahrávání je povoleno jen vám, případně uživatelům, +kterým to povlíte přes webové rozhraní. +Po úspěšném nahrání lze nahrávat další verze balíčku, ale musí být novější +než ta, co už na PyPI je. Nejde tedy jednou nahraný balíček přepsat.

+

Svůj balíček najdete na https://test.pypi.org/project/<název_balíčku>/.

+

Pro nahrání na opravdovou PyPI stačí vynechat -r testpypi. +Zabírat jména na opravdové PyPI jen tak není hezké vůči ostatním Pythonistům; +registrujte tedy prosím jen balíčky, které budou nějak pro ostatní užitečné.

+

Instalace pomocí pip +# +

+

Projekt nahraný na PyPI by mělo jít nainstalovat pomocí pipu. +V případě použití ostré verze PyPI stačí k instalaci zadat název balíčku:

+
(__venv__)$ python -m pip install <název_balíčku>
+

Pokud však použijeme testovací PyPI, je nutné pipu říct, aby balíček hledal tam. +Postup uvedený v dokumentaci není +v tomto případě nejvhodnější, protože z testovací PyPI vezme jak náš balíček, +tak i případné závislosti, které mohou být zastaralé, rozbité či jinak škodlivé.

+

Lepší by bylo, kdyby pip nainstaloval závislosti z ostré PyPI a na testovací +hledal jen náš projekt. Toho se dá docílit přepínačem --extra-index-url.

+
(__venv__)$ python -m pip install --extra-index-url https://test.pypi.org/pypi <název_balíčku>
+

V tomto případě pip nejdřív prohledá ostrou PyPI, a pokud nenajde požadovaný +balíček, použije testovací PyPI. Zde je potřeba dávat pozor na název projektu, +protože případné konflikty mezi ostrou a testovací PyPI se nekontrolují. +Pokud tedy máme projekt na testovací PyPI a na ostré existuje projekt se +stejným názvem, nainstaluje se ten z ostré verze.

+

V případě, že tento problém nastane, je možné ho částečně obejít specifikací +verze instalovaného balíčku:

+
(__venv__)$ python -m pip install --extra-index-url https://test.pypi.org/pypi <název_balíčku>==0.3
+

Pokud u duplicitního projektu na ostré PyPI neexistuje požadovaná verze, +nainstaluje se náš balíček z testovací PyPI.

+

Jiná možnost je zadat přímo cestu k archivu s balíčkem místo jeho názvu. +Zde pak na umístění balíčku ani verzi nezáleží:

+
(__venv__)$ python -m pip install https://test-files.pythonhosted.org/packages/.../<název_balíčku>-0.3.tar.gz
+

Odkaz na archiv se dá najít na informační stránce o našem projektu na PyPI.

+

Datové soubory +# +

+

Některé moduly kromě samotného kódu potřebují i datové soubory. +Například aplikace ve Flasku potřebují templates. +Taková data se dají do balíčku přidat parametrem package_data:

+
setup(...,
+    packages=['hello_flask'],
+    ...
+    package_data={'hello_flask': ['templates/*.html']},
+)
+

Další informace jsou odkázané v dokumentaci.

+

Wheel: Binární balíčky +# +

+

Zatím jsme se zabývali jen zdrojovými balíčky (sdist). +Existují ale i balíčky „zkompilované” – binární (bdist). +Když se instaluje zdrojový balíček, vykonává se kód ze souboru setup.py. +Binární balíček se místo toho jen rozbalí na patřičné místo.

+

Z historických důvodů existuje několik různých druhů binárních distribucí, +v současné době je ale důležitá pouze možnost bdist_wheel:

+
(__venv__)$ python setup.py bdist_wheel
+

Výsledek je v souboru dist/*.whl.

+

Pokud vám příkaz nefunguje, nainstalujte balík wheel.

+

Obsah binárního balíčku typu wheel můžete prozkoumat, je to obyčejný ZIP.

+

Naše programy jsou zatím platformně nezávislé a ve wheelu, +i když se jmenuje binární, žádné binární soubory nejsou. +To se ale změní, až se budeme zabývat tvorbou modulů v jazyce C: +sdist pak obsahuje zdrojové soubory a bdist_wheel zkompilované moduly.

+

Potom je dobré distribuovat oba dva – každý má své výhody:

+
    +
  • sdist jde nainstalovat na různých operačních systémech i procesorových +architekturách,
  • +
  • sdist tradičně obsahuje soubory jako LICENSE a README, ale
  • +
  • wheel při instalaci nepotřebuje např. překladače C (všechno už je přeložené +pro konkrétní OS a architekturu), a
  • +
  • wheel se rychleji instaluje.
  • +
+

Proces vydání složitějšího softwaru pak může vypadat takto:

+
(__venv__)$ rm dist/*
+(__venv__)$ python setup.py sdist bdist_wheel
+[... kontrola vytvořených balíčků v „čistém“ virtualenvu ...]
+(__venv__)$ python -m twine upload dist/*
+

Další +# +

+

K balíčkování existuje obsáhlá dokumentace. +Budete-li chtít dělat něco, co v tomto kurzu není, podívejte se tam!

\ No newline at end of file diff --git a/lessons/docs/index.html b/lessons/docs/index.html new file mode 100644 index 00000000..a45c1172 --- /dev/null +++ b/lessons/docs/index.html @@ -0,0 +1,428 @@ +

Dokumentace +# +

+

Jednou ze zásadních součástí každého kvalitního Python projektu je dokumentace. +Protože chceme, abyste vytvářeli kvalitní projekty, podíváme se tedy i na +dokumentaci.

+

Sphinx +# +

+

Nejpoužívanějším nástrojem na vytváření dokumentace Python projektů je Sphinx. +Když jste se dívali do dokumentace Flasku, requests, clicku, flexmocku, pytestu, +betamaxu či Pythonu samotného, viděli jste dokumentaci vytvořenou ve Sphinxu.

+

Pro vytvoření základní kostry dokumentace se používá jednoduchý průvodce, +sphinx-quickstart.

+

Postupujte podle následující ukázky. Jsou v ní zobrazeny jen věci, +kde nestačí nechat výchozí hodnota; u ostatních otázek (dostanete-li je) +stačí výchozí hodnotu potvrdit (Enter).

+
$ . __venv__/bin/activate
+(__venv__) $ python -m pip install sphinx
+(__venv__) $ mkdir docs
+(__venv__) $ cd docs
+(__venv__) $ sphinx-quickstart
+Welcome to the Sphinx 1.8.1 quickstart utility.
+
+Please enter values for the following settings (just press Enter to
+accept a default value, if one is given in brackets).
+
+Selected root path: .
+
+You have two options for placing the build directory for Sphinx output.
+Either, you use a directory "_build" within the root path, or you separate
+"source" and "build" directories within the root path.
+> Separate source and build directories (y/n) [n]: 
+
+The project name will occur in several places in the built documentation.
+> Project name: coolthing
+> Author name(s): Pythonista Dokumentarista
+> Project release []:  0.1
+
+...
+
+Finished: An initial directory structure has been created.
+

Průvodce vytvoří ve složce docs několik souborů:

+
    +
  • conf.py – konfigurační soubor,
  • +
  • index.rst – vlastní text dokumantace,
  • +
  • Makefile, make.bat – spouštěcí soubory,
  • +
  • _static – adresář na obrázky, CSS apod.,
  • +
  • _templates – Adresář na vlastní šablony,
  • +
  • _build – adresář pro výstup, tedy hotovou dokumentaci.
  • +
+

Do gitu patří všechny nyní vytvořené soubory, kromě složky docs/_build, +která by měla být ignorována.

+

Zatím se nebudeme zabývat obsahem těchto souborů, ale zkusíme základní kostru +dokumentace sestavit do HTML.

+

Sphinx umí generovat dokumentaci ve více formátech (LaTeX, +manuálové stránky atd.), pro nás bude podstatné především HTML.

+
(__venv__) $ make html
+...
+The HTML pages are in _build/html.
+

Ve zmíněné složce byste měli najít index.html, ten si můžete prohlédnout +v prohlížeči.

+

Textový obsah v dokumentaci +# +

+

Text dokumentace začíná v souboru index.rst a píše se ve značkovacím formátu +reStructuredText neboli rst. +Ten se od Markdownu liší v syntaxi, která je komplikovanější na +psaní, ale umožňuje dělat komplexnější věci.

+

Dokumentaci lze psát i ve formátu Markdown, +ale tato možnost je poměrně nová. +Jazyk Markdown nebyl navržen pro složitější strukturované texty +a nepodporuje přímo všechny možnosti, které Sphinx nabízí. +V dokumentaci se tak dočtete, jak chybějící možnosti doplnit +vložením reStructuredText do Markdownového dokumentu. +My budeme používat reStructuredText.

+

Pro přehled o tom, co reStructuredText umí a jakou má syntaxi, +můžete použít přehled z dokumentace Sphinxu, případně tahák.

+

V index.rst je seznam kapitol:

+
.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+

Tam můžete přidat další kapitoly:

+
.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   intro
+   tutorial/foo
+   tutorial/bar
+   ...
+

Soubory s kapitolami je třeba vytvořit ve složce docs s příponou .rst +(např. tutorial/foo.rst). +Text lze pak přidávat samozřejmě do těchto souborů i do +index.rst.

+

Chcete-li odkazovat na některou sekci, označíme si ji pomocí .. _label::

+
.. _my-reference-label:
+
+Section to cross-reference
+--------------------------
+
+This is the text of the section.
+

Poté na ni lze odkazovat odkudkoli z dokumentace pomocí +konstrukce ref:

+
It refers to the section itself, see :ref:`my-reference-label`.
+It could refer to a different section as well :)
+

Co do dokumentace psát +# +

+

Teď, když víte jak něco napsat, pojďme si povědět co vlastně psát. +K čemu dokumentace vlastně je?

+

Dobrá dokumentace vysvětluje, proč a jak by váš projekt měl někdo používat. +Jak říká Eric Holscher v jedné své prezentaci,

+
+

Když lidi neví, že váš projekt existuje,
+nebudou ho používat.
+Když lidi nepřijdou na to, jak váš projekt nainstalovat,
+nebudou ho používat.
+Když lidi nepřijdou na to, jak váš projekt použít,
+nebudou ho používat.

+
+

Pokud pracujete v malém týmu, teoreticky jde to všechno kolegům prostě říct, +ale potom se musíte spoléhat na to, že to nezapomenete (a neodejdete z týmu). +Mnohem lepší je dokumentaci sepsat co nejdřív, dokud máte všechno čerstvě +v hlavě.

+

Nechce-li se vám nastavovat Sphinx, můžete informace napsat aspoň do malého +README. Ale i tam by měl být stejný druh informací jako ve „velké“ dokumentaci.

+

Na první stránce dokumentace (nebo v README) typicky najdeme:

+
    +
  • krátký text o tom, co projekt dělá;
  • +
  • ukázku – u knihovny příklad kódu, u aplikace screenshot, u webové aplikace +odkaz na běžící instanci;
  • +
  • návod na instalaci;
  • +
  • odkazy na zbytek dokumentace;
  • +
  • odkazy pro přispěvatele – kde je repozitář, kde nahlásit chybu;
  • +
  • licenci.
  • +
+

Delší dokumentace knihoven pak většinou obsahuje:

+
    +
  • tutoriál – návod, který uživatele provede použitím a možnostmi knihovny;
  • +
  • popis architektury, návrhu, použitých konceptů;
  • +
  • dokumentaci API – popis všech veřejných modulů, tříd, funkcí a podobně;
  • +
  • podrobný návod jak přispívat.
  • +
+

Nastavení a rozšíření +# +

+

Průvodce sphinx-quickstart generuje soubor s nastavením, conf.py, +ve kterém můžete měnit nastavení Sphinxu a jeho rozšíření, včetně detailů +jako jméno a verze projektu.

+

Průvodce automaticky aktivuje tři rozšíření, která jsou obecně užitečná. +To se ale může v jiných verzích Sphinxu měnit, proto teď nastavení +zkontrolujte a případně rozšíření doplňte:

+
extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.doctest',
+    'sphinx.ext.intersphinx',
+]
+

doctest +# +

+

doctest je modul ze standardní knihovny, který najde v dokumentaci bloky kódu +a otestuje, jestli odpovídají ukázanému výstupu. +Rozšíření sphinx.ext.doctest integruje doctest do dokumentů +ve formátu reStructuredText.

+

Pro nás to bude způsob, jak testovat dokumentaci – tedy jestli jsou ukázky +kódu v ní stále platné. +Dá se sice použít i k testování samotného kódu, ale na to existují +lepší nástroje.

+

Můžete to dělat dvěma způsoby. První je mít v dokumentaci +příklad vypadající jako interaktivní konzole. +Takový příklad nemusí být odsazený ani ničím uvozený; stačí >>> na začátku.

+
>>> 1 + 1
+2
+

Doctest v tomto případě otestuje, že vše funguje, jak má. +V tomto případě se provede součet a zkontroluje se, zda výsledek je 2.

+

Druhý způsob je mít v dokumentaci nejdříve kód:

+
print('foo')
+

A dále někde jinde výstup volání:

+
foo
+

K tomu všemu složí několik direktiv:

+

.. testsetup:: +# +

+

Direktiva pro potřebný kód, který se musí provést, aby příklad fungoval, ale +nebude v dokumentaci zobrazen (např. kód pro vytvoření falešného objektu, +import...).

+

.. testcleanup:: +# +

+

Podobná direktiva jako .. testsetup:: provedená po skončení testů. +V dokumentaci nebude kód zobrazen.

+

.. doctest:: +# +

+

Test s interaktivní konzolí. V dokumentaci bude zobrazen, pokud nepoužijete flag +:hide:.

+

.. testcode:: +# +

+

Kód testu bez interaktivní konzole, co chcete kontrolovat, musíte dát na +standardní výstup. V dokumentaci bude zobrazen, pokud nepoužijete flag +:hide:.

+

.. testoutput:: +# +

+

Výstup posledního testcode bloku. V dokumentaci bude kód zobrazen, pokud +nepoužijete flag :hide:.

+

Kompletní příklad +# +

+

Zde můžete vidět výše zmíněné direktivy použité dohromady. +Jedná se o umělý příklad, kdy použitou třídu připravíme v direktivě testsetup. +V praxi pak doctestem testujeme, jestli naše dokumentace odpovídá chování +naší implementace, třídu Parrot bychom tedy odněkud naimportovali.

+
The parrot module
+=================
+
+.. testsetup::
+
+   class Parrot:
+       def voom(self, voltage):
+           print('This parrot wouldn\'t voom if you put {} volts through it!'.format(voltage))
+
+       def die(self):
+           return 'RIP'
+
+
+   parrot = Parrot()
+
+The parrot module is a module about parrots.
+
+Doctest example:
+
+.. doctest::
+
+   >>> parrot.voom(3000)
+   This parrot wouldn't voom if you put 3000 volts through it!
+
+Test-Output example:
+
+.. testcode::
+
+   parrot.voom(3000)
+
+This would output:
+
+.. testoutput::
+
+   This parrot wouldn't voom if you put 3000 volts through it!
+
+You can use other values:
+
+.. testcode::
+
+   parrot.voom(230)
+
+.. testoutput::
+   :hide:
+
+   This parrot wouldn't voom if you put 230 volts through it!
+
+
+.. testcleanup::
+
+   parrot.die()
+

Testy se také dají např. zařazovat do skupin. Více najdete +v dokumentaci.

+
(__venv__) $ make doctest
+...
+Document: intro
+---------------
+1 items passed all tests:
+   3 tests in default
+3 tests in 1 items.
+3 passed and 0 failed.
+Test passed.
+1 items passed all tests:
+   1 tests in default (cleanup code)
+1 tests in 1 items.
+1 passed and 0 failed.
+Test passed.
+
+Doctest summary
+===============
+    3 tests
+    0 failures in tests
+    0 failures in setup code
+    0 failures in cleanup code
+...
+

Import z vlastního kódu +# +

+

Pokud nemáte nainstalovaný vlastní balíček a budete z něj chtít v doctestu +importovat, pravděpodobně dostanete ImportError. +V takovém případě pomůže drobná editace na začátku conf.py. +Musíte přidat adresář, ze kterého lze váš kód importovat, do sys.path. +Pokud jste postupovali podle návodu výše, máte dokumentaci v adresáři docs, +je tedy potřeba přidat nadřazený adresář (..):

+
# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+import sys
+sys.path.insert(0, os.path.abspath('..'))
+

Travis CI +# +

+

Neexistuje zatím unifikovaný způsob, jak specifikovat závislosti pro sestavení +dokumentace. Proto, pokud chcete mít nějaký jednoduchý způsob, jak pouštět +doctesty na Travisu, vytvořte například soubor docs/requirements.txt +a do něj dejte závislosti potřebné pro sestavení dokumentace. +Je na vás, jestli tam budou pouze extra závislosti oproti těm v setup.py +(většinou pouze sphinx), nebo všechny závislosti, aby šel použít soubor +samostatně.

+

Poté na Travisu můžete udělat něco jako:

+
language: python
+python:
+- '3.7'
+install:
+- python setup.py install
+- pip install -r docs/requirements.txt
+script:
+- python setup.py test --addopts -v
+- cd docs && make doctest
+

Chcete-li jít s dobou, můžete vyzkoušet strukturovaný způsob závislostí +pro vývoj pomocí extras. Aktuálně pro to neexistuje standard, +ale vypadá to, že následující způsob je nejlepší kandidát na +standardizaci.

+

Do setup.py přidejte:

+
extras_require={
+    'dev':  ["sphinx"],
+}
+

Projekt pak lze nainstalovat pomocí .[dev] (tedy jméno balíčku a za ním +jméno extras v hranatých závorkách):

+
install:
+- python -m pip install .[dev]
+

autodoc +# +

+

Pro dokumentaci API lze použít sphinx.ext.autodoc, další rozšíření Sphinxu, +které průvodce přidává automaticky.

+

Nemáte-li toto rozšíření povolené, přidejte jej do conf.py:

+
extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.doctest',
+    'sphinx.ext.intersphinx',
+]
+

Rozšíření autodoc se používá takto:

+
.. automodule:: mymodule
+   :members:
+

Tento příklad na dané místo vygeneruje dokumentaci složenou z dokumentačních +řetězců jednotlivých funkcí, tříd a metod v modulu mymodule.

+

Pokud chcete selektivně vybrat, dokumentaci čeho chcete generovat, +můžete použít i +jiné direktivy.

+

Pro vygenerování hezké struktury si můžete pomoci příkazem sphinx-apidoc:

+
(__venv__) $ sphinx-apidoc -o docs mymodule
+

V dokumentačních řetězcích samozřejmě můžete použít reStructuredText a je to +dokonce žádoucí.

+

Zde je ukázka z betamaxu (Copyright 2013 Ian Cordasco):

+
class Betamax:
+
+    """This object contains the main API of the request-vcr library.
+
+    This object is entirely a context manager so all you have to do is:
+
+    .. code::
+
+        s = requests.Session()
+        with Betamax(s) as vcr:
+            vcr.use_cassette('example')
+            r = s.get('https://httpbin.org/get')
+
+    Or more concisely, you can do:
+
+    .. code::
+
+        s = requests.Session()
+        with Betamax(s).use_cassette('example') as vcr:
+            r = s.get('https://httpbin.org/get')
+
+    This object allows for the user to specify the cassette library directory
+    and default cassette options.
+
+    .. code::
+
+        s = requests.Session()
+        with Betamax(s, cassette_library_dir='tests/cassettes') as vcr:
+            vcr.use_cassette('example')
+            r = s.get('https://httpbin.org/get')
+
+        with Betamax(s, default_cassette_options={
+                're_record_interval': 1000
+                }) as vcr:
+            vcr.use_cassette('example')
+            r = s.get('https://httpbin.org/get')
+    """
+

Existují různé způsoby, jak dokumentovat argumenty, návratové hodnoty apod. +Zvídavým studentům doporučujeme podívat se na rozšíření Napoleon.

+

Odkazy na třídy a moduly +# +

+

Máte-li zdokumentovaný modul, funkci, třídu, metodu apod., je možné na ni +odkázat pomocí konstrukce :mod:, :func:, :class:, :meth: a dalších +ze Sphinxové domény Python:

+
To test the parrot's electrical resistance, use :meth:`parrot.voom()`.
+

V této části dokumentace Sphinxu též najdete způsob, jak dokumentovat API +bez použití autodoc.

+

Všechny zdokumentované objekty se automaticky přidávají do rejstříku. +Chcete-li do rejstříku přidat něco navíc, použijte direktivu index.

+

README.rst +# +

+

Když už se stejně zabýváme reStructuredTextem, je dobré váš README přepsat +nebo převést do stejného formátu. Na PyPI pak bude váš projekt vypadat lépe.

+

Při přejmenování na README.rst dejte pozor na patřičné změny v setup.py.

+

Read the Docs +# +

+

Pokud svůj repositář na GitHubu změníte na veřejný, můžete využít službu +Read the Docs k hostování dokumentace ve Sphinxu. +Dokumentace se sestaví při každém pushnutí na GitHub.

+

Pokud Read the Docs použijete, nezapomeňte na dokumentaci odkázat +z README.rst.

\ No newline at end of file diff --git a/lessons/projects/snake/static/apple.png b/lessons/drawing/apple.png similarity index 100% rename from lessons/projects/snake/static/apple.png rename to lessons/drawing/apple.png diff --git a/lessons/snake/drawing/static/caterpillar.png b/lessons/drawing/caterpillar.png similarity index 100% rename from lessons/snake/drawing/static/caterpillar.png rename to lessons/drawing/caterpillar.png diff --git a/lessons/snake/drawing/static/coords-blocks.svg b/lessons/drawing/coords-blocks.svg similarity index 100% rename from lessons/snake/drawing/static/coords-blocks.svg rename to lessons/drawing/coords-blocks.svg diff --git a/lessons/snake/drawing/static/coords-px.svg b/lessons/drawing/coords-px.svg similarity index 100% rename from lessons/snake/drawing/static/coords-px.svg rename to lessons/drawing/coords-px.svg diff --git a/lessons/projects/snake/static/coords.svg b/lessons/drawing/coords.svg similarity index 100% rename from lessons/projects/snake/static/coords.svg rename to lessons/drawing/coords.svg diff --git a/lessons/projects/snake/static/green.png b/lessons/drawing/green.png similarity index 100% rename from lessons/projects/snake/static/green.png rename to lessons/drawing/green.png diff --git a/lessons/drawing/index.html b/lessons/drawing/index.html new file mode 100644 index 00000000..08e65c08 --- /dev/null +++ b/lessons/drawing/index.html @@ -0,0 +1,419 @@ +

Nakresli mi hada +# +

+

Většina videoher má vlastní svět – spoustu čísel, textů, seznamů a jiných +datových objektů, které popisují všechno, co ve hře je – celý stav hry. +Tenhle stav se časem mění, ať už automaticky nebo podle akcí hráče. +A docela často – většinou zhruba šedesátkrát za vteřinu – se stav hry převede +na obrázek, který se hráčovi ukáže.

+

Abys mohl/a zobrazit hada, budeš napřed muset definovat stav hry – zadat +to, co se má vykreslovat.

+

Zkus se zamyslet, co všechno bude ten stav obsahovat: co všechno si počítač +musí o hře „pamatovat“, aby mohl aktuální stav zobrazit?

+

Bude potřebovat například aktuální polohu všech částí hada: kde má začátek? +Kroutí se doprava nebo doleva? Jak je dlouhý? +Naopak barvu hada se stavu ukložit nepotřebuješ – každý had v téhle hře bude +stejný.

+

Napadne tě, jak polohu hada zapsat pomocí čísel, seznamů a dalších základních +datových typů?

+

Reprezentace hada +# +

+

Asi nejjednodušší způsob, jak si v počítači „zapamatovat“ herního hada, +je pomocí seznamu souřadnic.

+

Pamatuješ si ze školy na kartézské souřadnice? +To je taková ta část matematiky, co možná vypadala že nemá praktické využití. +Pro počítačovou grafiku jsou ale souřadnice to co pro češtinu písmenka. +Pojďme si je osvěžit.

+

Každý bod v rovině (třeba na obrazovce!) +je možné popsat dvěmi čísly: x-ovou a y-ovou souřadnicí. +Ta x-ová říká, jak moc vlevo je bod od nějakého počátku, +ta y-ová udává jak moc je nahoře. +My za onen „počátek“ zvolíme roh okýnka, ve kterém se bude plazit náš had.

+

Na rozdíl od školní geometrie se had bude plazit po čtverečkové mřížce. +Je to jako na šachovnici – když jde pěšec na D5, D značí, jak moc je to +políčko vlevo od okraje a 5 jak moc „nahoře“.

+

Tady je had, který začíná na souřadnicích (1, 2) a hlavu má na (4, 5):

+

Had na „šachovnici“ se souřadnicemi

+

Možná si všimneš, že matematický zápis souřadnic – (1, 2) – odpovídá +způsobu, jak se v Pythonu píšou n-tice. +To není náhoda! +Dvojice čísel je perfektní způsob, jak uložit souřadnice kousku hada.

+

Had má ale kousků víc, a jinak dlouzí hadi jich budou mít různý počet. +Na to je dobré použít seznam. Seznam souřadnic. +A protože souřadnice pro nás budou dvojice čísel, +seznam souřadnic bude seznam dvojic čísel.

+

Had z obrázku výše bude v Pythonu vypadat takto:

+
snake = [(1, 2), (2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (4, 5)]
+

Tohle je reprezentace hada – to, co je z hlediska hry potřeba o konkrétním +hadovi vědět.

+

Počítačům (a programátorům?) to takhle stačí. +My si to ale zkusme zobrazit barevně, ať hadovi rozumí i hráč naší budoucí hry.

+

Logické a zobrazovací souřadnice +# +

+

U vykreslování hada musíme vyřešit jeden základní problém: +převod logických souřadnic na souřadnice obrazovky.

+

Displeje počítačů fungují podobně jako naše souřadnicová „šachovnice“: +jsou to čtvercové mřížky plné políček. +Každému políčku – pixelu – lze nastavit barvu. +Hlavní rozdíl proti šachovnici je v tom, že pixelů na obrazovce je mnohem, +mnohem víc.

+

Kdyby byl každý „herní“ čtvereček 10×10 pixelů velký, +tak hlava hada z ukázky, která má „herní“ souřadnice (4, 5), +se na obrazovku bude vykreslovat na čtverečku, který začíná na (40, 50):

+

Had na „šachovnici“ se souřadnicemi obrazovky

+

A ocas s „herními“ (logickými) souřadnicemi (1, 2) se vykreslí na čtvereček +se souřadnicemi (10, 20).

+

Sázení čtverečku +# +

+

Na to, abychom hada vykreslili, použijeme okýnko z Pygletu. +Tady je základní kostra programu Pyglet, které už bys měl/a rozumět.

+

Udělej si nový, prázdný adresář na hadí hru, a kostru si +zkopíruj do souboru ui.py. +Budeme ji dál rozvíjet.

+
import pyglet
+
+window = pyglet.window.Window()
+
+@window.event
+def on_draw():
+    window.clear()
+
+pyglet.app.run()
+
+Stáhni si soubor [green.png](naucse:static?filename=green.png) – zelený čtvereček – +a dej ho do adresáře, kam píšeš kód. + +

Pod import pyglet přidej řádek, který tento obrázek načte.

+
green_image = pyglet.image.load('green.png')
+

Potom zkus dovnitř do funkce on_draw přidat vykreslení obrázku na souřadnice +(40, 50), velikosti 10.

+
    green_image.blit(40, 50, width=10, height=10)
+

Program spusť (cd do nového adresáře; python ui.py). Funguje? +(Je docela důležité, aby fungoval – nevidíš-li zelený čtvereček, +nečti dál a program radši oprav.)

+

Jak vidíš, čtvereček je docela malý. +Budeme radši používat čtverečky větší, řekněme 64 pixelů.

+

To číslo je „střelené od boku“. +V programu ho použijeme několikrát, a možná ho později budeš chtít upravit. +Uložíme si ho proto do konstanty – proměnné, kterou nebudeme měnit. +Konstanty se tradičně pojmenovávají velkými písmeny, a píšou se hned za řádek +import (i když to není technicky nutné). +Přidej tedy za import řádek:

+
TILE_SIZE = 64
+

… a ve volání green.blit velikost čtverečku zohledni:

+
    green_image.blit(4 * TILE_SIZE, 5 * TILE_SIZE,
+                     width=TILE_SIZE, height=TILE_SIZE)
+

Povedlo se? Máš čtvereček? +Jestli ne, zkus si program celý, řádek po řádce, projít a zkontrolovat. +Nebo ho porovnej se vzorovým řešením (což je rychlejší varianta, ale míň +se naučíš).

+
+

Řešení

+ + +
+ +

Sázení hada +# +

+

Zkus teď na začátek programu – těsně pod import a konstantu – přidat +definici hada:

+
snake = [(1, 2), (2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (4, 5)]
+

A ve funkci draw vykresli všechny čtverečky hada. +Vzpomeň si, že seznam dvojic můžeš „projít“ pomocí cyklu for a „rozbalení“ +n-tice:

+
for x, y in snake:
+    ...
+

Funguje to? Vidíš v tom – aspoň zhruba – hada +(i když je poskládaný ze čtverečků)?

+

Had na „šachovnici“ a ukázka programu

+

Jestli ne, nezoufej, zkontroluj si to znovu, poptej se na radu. +Řešení využij až jako krajní možnost, jak pokračovat dál – nebo na kontrolu +správného řešení.

+
+

Řešení

+ + +
+ +

Krmení +# +

+ + +Aby bylo ve hře co dělat, budeme potřebovat pro hada krmení. +Stáhni si do adresáře s projektem obrázek `apple.png` (ať už jednoduchý +čtvereček nebo detailnější obrázek), a zkus vykreslit jídlo třeba +na následující souřadnice: + +
food = [(2, 0), (5, 1), (1, 4)]
+
+

Řešení

+ + +
+ +

Používáš-li detailnější obrázek, možná si všimneš, že má trošičku „zubaté“ hrany. +To je dáno způsobem, jakým v Pygletu obrázek vykreslujeme. +Úplné vysvětlení by zabralo příliš času, proto ukážu jen řešení. +Až se naučíš grafiku víc do hloubky, pochopíš co se tu děje :)

+

Do funkce on_draw, hned za clear, dej následující dva řádky:

+
    # Lepší vykreslování (pro nás zatím kouzelné zaříkadlo)
+    pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
+    pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
+

Načítání kousků hada +# +

+

Teď, když umíš kreslit hada ze čtverců, zkusíme ho udělat hezčího. +Stáhni si archiv snake-tiles.zip +a rozbal si ho tak, aby adresář snake-tiles s obrázky byl na stejné úrovni +jako program s hrou. +Struktura adresáře by měla vypadat takhle:

+

Adresářová struktura

+

V archivuje spousta „kousků“ hada, které můžeme vykreslovat místo zelených +čtverečků. +Kousky vypadají následovně. +Všimni si pojmenování – každý kousek hada buď spojuje dvě strany obrázku, +nebo stranu obrázku s hlavou či ocasem. +Obrázek se jmenuje odkud-kam.png.

+

Kousky hada

+

Co jsou taková ta divná „hadí vajíčka”? +To je pro přímad, že by had byl jen jedno políčko dlouhý – a tedy měl hlavu +i ocas na stejném políčku. +V naší hře se do takového stavu nedostaneme (had bude začínat s délkou 2), +ale může se stát, že se do něj dostaneš omylem při vývoji hry. +Když jsou obrázky k dispozici, lépe pak zjišťuješ, co je špatně.

+

Pojďme si teď tyhle obrázky načíst. +Šlo by to dělat postupně, třeba takhle:

+
bottom_left = pyglet.image.load('snake-tiles/bottom-left.png')
+bottom_right = pyglet.image.load('snake-tiles/bottom-right.png')
+bottom_top = pyglet.image.load('snake-tiles/bottom-top.png')
+...
+

Ale obrázků je spousta, tímhle způsobem by to bylo zdlouhavé a nejspíš bys +na některý zapomněl/a.

+

Proto Pythonu řekneme, aby nám dal všechny soubory s koncovkou .png v daném +adresáři. +Na to se dá použít třída Path z modulu pathlib. +Zkus si napsat (do nového souboru, třeba experiment.py) tento kód +a spustit ho. +Dokážeš vysvětlit, co dělá?

+
from pathlib import Path
+
+TILES_DIRECTORY = Path('snake-tiles')
+
+for path in TILES_DIRECTORY.glob('*.png'):
+    print(path)
+

My z každého souboru potřebujeme nejlépe jméno, tedy místo +snake-tiles/right-head.png jenom right-head. +Na to naštěstí existuje atribut stem (kořen, t.j. jméno bez přípony). +Místo print(path) použij:

+
    print(path.stem)
+

Funguje? Máš vypsané všechny možné kousky hada?

+

Teď budeme chtít načíst obrázky do slovníku. +Klíče slovníku, podle kterých budeme vyhledávat, budou jména, která jsi +právě vypsal/a. +Hodnoty pak budou pygletí obrázky, které ve hře můžeš rovnou vykreslit.

+

Začni s prázdným slovníkem, {}, a v cyklu for do něj postupně přidávej +záznamy. +Pak slovník vypiš.

+

Až to budeš mít, měl by výpis vypadat asi takhle:

+
{'right-tongue': <ImageData 64x64>, 'top-tongue': <ImageData 64x64>,
+ 'right-top': <ImageData 64x64>, 'left-bottom': <ImageData 64x64>,
+ 'tail-left': <ImageData 64x64>, 'bottom-tongue': <ImageData 64x64>,
+ 'left-top': <ImageData 64x64>, 'bottom-bottom': <ImageData 64x64>,
+ ...
+
+

Řešení

+ + +
+ +

Housenka +# +

+

A teď zkus načtení obrázků začlenit do programu s hadem!

+

Všechny importy patří nahoru, konstanty pod ně, a dál pak zbytek kódu. +Vypisovat načtený slovník ve hře nemusíš, zato místo green_image +pak ve vykreslovací funkci použij třeba snake_tiles['tail-head'].

+

Místo čtverečků se teď objeví kuličky – místo hada budeš mít „housenku“.

+

Housenka

+
+

Řešení

+ + +
+ +

Jak vybrat čtverečky? +# +

+

Místo toho, aby byl všude stejný kousek hada, +ale budeme chtít vybrat vždycky ten správný.

+

Jak na to?

+

Podle čeho ho vybrat?

+

Vytvoř soubor smery.py a napiš do něj:

+
snake = [(1, 2), (2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (4, 5)]
+
+for x, y in snake:
+    print(x, y)
+

Tenhle kód vypisuje souřadnice:

+
1 2
+2 2
+3 2
+3 3
+3 4
+3 5
+4 5
+

Zkus vymyslet, jak by se tenhle kód dal změnit, aby vypisoval ke každé +souřadnici směr k předchozímu a následujícímu políčku – tedy odkud a kam +každý kousek hada „vede“. +Takhle:

+
1 2 tail right
+2 2 left right
+3 2 left top
+3 3 bottom top
+3 4 bottom top
+3 5 bottom right
+4 5 left head
+

Toto je těžký úkol. +Nepředpokládám, že ho zvládneš vyřešit hned, i když všechny potřebné informace +a nástroje k tomu znáš. +Zkus nad tím ale přemýšlet, nech si to rozležet v hlavě třeba přes noc, +vrať se k materiálům k předchozím lekcím (hlavně k úvodu do Pythonu), +zkoušej a objevuj… A časem na to přijdeš.

+

Až se to stane, zkus své řešení co nejvíc zjednodušit a pak ho zakomponovat +do vykreslovací funkce. +To by už nemělo být příliš složité:

+
    for ??? in ???snake???:
+        ...
+        x = ???
+        y = ???
+        odkud = ???
+        kam = ???
+        ...
+
+        snake_tiles[odkud + '-' + kam].blit(
+            x * TILE_SIZE, y * TILE_SIZE, width=TILE_SIZE, height=TILE_SIZE)
+

Soubor smery.py po vyřešení nemaž, bude se ti pak hodit.

+

Odměnou za vyřešení tohoto úkolu ti bude had místo housenky.

+

Než na to přijdeš, zbytek programu ti neuteče. +Housenka je úplně stejně hratelná jako had, jen jinak vypadá. +Klidně přejdi na další část – logiku hry – s housenkou.

\ No newline at end of file diff --git a/lessons/snake/drawing/static/red.png b/lessons/drawing/red.png similarity index 100% rename from lessons/snake/drawing/static/red.png rename to lessons/drawing/red.png diff --git a/lessons/snake/drawing/static/screenshot-dir.png b/lessons/drawing/screenshot-dir.png similarity index 100% rename from lessons/snake/drawing/static/screenshot-dir.png rename to lessons/drawing/screenshot-dir.png diff --git a/lessons/snake/drawing/static/screenshot-finished.png b/lessons/drawing/screenshot-finished.png similarity index 100% rename from lessons/snake/drawing/static/screenshot-finished.png rename to lessons/drawing/screenshot-finished.png diff --git a/lessons/snake/drawing/static/screenshot-initial.png b/lessons/drawing/screenshot-initial.png similarity index 100% rename from lessons/snake/drawing/static/screenshot-initial.png rename to lessons/drawing/screenshot-initial.png diff --git a/lessons/projects/snake/static/snake-tiles.png b/lessons/drawing/snake-tiles.png similarity index 100% rename from lessons/projects/snake/static/snake-tiles.png rename to lessons/drawing/snake-tiles.png diff --git a/lessons/projects/snake/static/snake-tiles.zip b/lessons/drawing/snake-tiles.zip similarity index 100% rename from lessons/projects/snake/static/snake-tiles.zip rename to lessons/drawing/snake-tiles.zip diff --git a/lessons/snake/drawing/static/snake-tiles/bottom-bottom.png b/lessons/drawing/snake-tiles/bottom-bottom.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/bottom-bottom.png rename to lessons/drawing/snake-tiles/bottom-bottom.png diff --git a/lessons/snake/drawing/static/snake-tiles/bottom-dead.png b/lessons/drawing/snake-tiles/bottom-dead.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/bottom-dead.png rename to lessons/drawing/snake-tiles/bottom-dead.png diff --git a/lessons/snake/drawing/static/snake-tiles/bottom-head.png b/lessons/drawing/snake-tiles/bottom-head.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/bottom-head.png rename to lessons/drawing/snake-tiles/bottom-head.png diff --git a/lessons/snake/drawing/static/snake-tiles/bottom-left.png b/lessons/drawing/snake-tiles/bottom-left.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/bottom-left.png rename to lessons/drawing/snake-tiles/bottom-left.png diff --git a/lessons/snake/drawing/static/snake-tiles/bottom-right.png b/lessons/drawing/snake-tiles/bottom-right.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/bottom-right.png rename to lessons/drawing/snake-tiles/bottom-right.png diff --git a/lessons/snake/drawing/static/snake-tiles/bottom-tongue.png b/lessons/drawing/snake-tiles/bottom-tongue.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/bottom-tongue.png rename to lessons/drawing/snake-tiles/bottom-tongue.png diff --git a/lessons/snake/drawing/static/snake-tiles/bottom-top.png b/lessons/drawing/snake-tiles/bottom-top.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/bottom-top.png rename to lessons/drawing/snake-tiles/bottom-top.png diff --git a/lessons/snake/drawing/static/snake-tiles/left-bottom.png b/lessons/drawing/snake-tiles/left-bottom.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/left-bottom.png rename to lessons/drawing/snake-tiles/left-bottom.png diff --git a/lessons/snake/drawing/static/snake-tiles/left-dead.png b/lessons/drawing/snake-tiles/left-dead.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/left-dead.png rename to lessons/drawing/snake-tiles/left-dead.png diff --git a/lessons/snake/drawing/static/snake-tiles/left-head.png b/lessons/drawing/snake-tiles/left-head.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/left-head.png rename to lessons/drawing/snake-tiles/left-head.png diff --git a/lessons/snake/drawing/static/snake-tiles/left-left.png b/lessons/drawing/snake-tiles/left-left.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/left-left.png rename to lessons/drawing/snake-tiles/left-left.png diff --git a/lessons/snake/drawing/static/snake-tiles/left-right.png b/lessons/drawing/snake-tiles/left-right.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/left-right.png rename to lessons/drawing/snake-tiles/left-right.png diff --git a/lessons/snake/drawing/static/snake-tiles/left-tongue.png b/lessons/drawing/snake-tiles/left-tongue.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/left-tongue.png rename to lessons/drawing/snake-tiles/left-tongue.png diff --git a/lessons/snake/drawing/static/snake-tiles/left-top.png b/lessons/drawing/snake-tiles/left-top.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/left-top.png rename to lessons/drawing/snake-tiles/left-top.png diff --git a/lessons/snake/drawing/static/snake-tiles/right-bottom.png b/lessons/drawing/snake-tiles/right-bottom.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/right-bottom.png rename to lessons/drawing/snake-tiles/right-bottom.png diff --git a/lessons/snake/drawing/static/snake-tiles/right-dead.png b/lessons/drawing/snake-tiles/right-dead.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/right-dead.png rename to lessons/drawing/snake-tiles/right-dead.png diff --git a/lessons/snake/drawing/static/snake-tiles/right-head.png b/lessons/drawing/snake-tiles/right-head.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/right-head.png rename to lessons/drawing/snake-tiles/right-head.png diff --git a/lessons/snake/drawing/static/snake-tiles/right-left.png b/lessons/drawing/snake-tiles/right-left.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/right-left.png rename to lessons/drawing/snake-tiles/right-left.png diff --git a/lessons/snake/drawing/static/snake-tiles/right-right.png b/lessons/drawing/snake-tiles/right-right.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/right-right.png rename to lessons/drawing/snake-tiles/right-right.png diff --git a/lessons/snake/drawing/static/snake-tiles/right-tongue.png b/lessons/drawing/snake-tiles/right-tongue.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/right-tongue.png rename to lessons/drawing/snake-tiles/right-tongue.png diff --git a/lessons/snake/drawing/static/snake-tiles/right-top.png b/lessons/drawing/snake-tiles/right-top.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/right-top.png rename to lessons/drawing/snake-tiles/right-top.png diff --git a/lessons/snake/drawing/static/snake-tiles/tail-bottom.png b/lessons/drawing/snake-tiles/tail-bottom.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/tail-bottom.png rename to lessons/drawing/snake-tiles/tail-bottom.png diff --git a/lessons/snake/drawing/static/snake-tiles/tail-dead.png b/lessons/drawing/snake-tiles/tail-dead.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/tail-dead.png rename to lessons/drawing/snake-tiles/tail-dead.png diff --git a/lessons/snake/drawing/static/snake-tiles/tail-head.png b/lessons/drawing/snake-tiles/tail-head.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/tail-head.png rename to lessons/drawing/snake-tiles/tail-head.png diff --git a/lessons/snake/drawing/static/snake-tiles/tail-left.png b/lessons/drawing/snake-tiles/tail-left.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/tail-left.png rename to lessons/drawing/snake-tiles/tail-left.png diff --git a/lessons/snake/drawing/static/snake-tiles/tail-right.png b/lessons/drawing/snake-tiles/tail-right.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/tail-right.png rename to lessons/drawing/snake-tiles/tail-right.png diff --git a/lessons/snake/drawing/static/snake-tiles/tail-tongue.png b/lessons/drawing/snake-tiles/tail-tongue.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/tail-tongue.png rename to lessons/drawing/snake-tiles/tail-tongue.png diff --git a/lessons/snake/drawing/static/snake-tiles/tail-top.png b/lessons/drawing/snake-tiles/tail-top.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/tail-top.png rename to lessons/drawing/snake-tiles/tail-top.png diff --git a/lessons/snake/drawing/static/snake-tiles/top-bottom.png b/lessons/drawing/snake-tiles/top-bottom.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/top-bottom.png rename to lessons/drawing/snake-tiles/top-bottom.png diff --git a/lessons/snake/drawing/static/snake-tiles/top-dead.png b/lessons/drawing/snake-tiles/top-dead.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/top-dead.png rename to lessons/drawing/snake-tiles/top-dead.png diff --git a/lessons/snake/drawing/static/snake-tiles/top-head.png b/lessons/drawing/snake-tiles/top-head.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/top-head.png rename to lessons/drawing/snake-tiles/top-head.png diff --git a/lessons/snake/drawing/static/snake-tiles/top-left.png b/lessons/drawing/snake-tiles/top-left.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/top-left.png rename to lessons/drawing/snake-tiles/top-left.png diff --git a/lessons/snake/drawing/static/snake-tiles/top-right.png b/lessons/drawing/snake-tiles/top-right.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/top-right.png rename to lessons/drawing/snake-tiles/top-right.png diff --git a/lessons/snake/drawing/static/snake-tiles/top-tongue.png b/lessons/drawing/snake-tiles/top-tongue.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/top-tongue.png rename to lessons/drawing/snake-tiles/top-tongue.png diff --git a/lessons/snake/drawing/static/snake-tiles/top-top.png b/lessons/drawing/snake-tiles/top-top.png similarity index 100% rename from lessons/snake/drawing/static/snake-tiles/top-top.png rename to lessons/drawing/snake-tiles/top-top.png diff --git a/lessons/exceptions/index.html b/lessons/exceptions/index.html new file mode 100644 index 00000000..a7f138e8 --- /dev/null +++ b/lessons/exceptions/index.html @@ -0,0 +1,189 @@ +

Výjimky +# +

+

Pojďme si prohloubit znalosti o chybách, neboli odborně o výjimkách +(angl. exceptions).

+

Podívej se na následující funkci:

+
def nacti_cislo():
+    """Získá od uživatele celé číslo a vrátí ho"""
+    odpoved = input('Zadej číslo: ')
+    return int(odpoved)
+

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

+
Traceback (most recent call last):
+  File "ukazka.py", line 4, in nacti_cislo
+    cislo = int(odpoved)
+ValueError: invalid literal for int() with base 10: 'cokolada'
+

Program volá funkci int() pro něco, co nedává smysl jako číslo. +Co s tím má chudák funkce int dělat? +Není žádná rozumná hodnota, kterou by mohla vrátit. +Převádění tohoto textu na celé číslo nedává smysl.

+

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

+
def nacti_cislo():
+    """Získá od uživatele celé číslo a vrátí ho"""
+    while True:
+        odpoved = input('Zadej číslo: ')
+        if obsahuje_jen_cislice(odpoved):
+            return int(odpoved)  # máme výsledek, funkce končí
+        else:
+            print('To nebylo číslo!')
+            # ... a zeptáme se znovu -- cyklus `while` pokračuje
+

Kde ale vzít funkci obsahuje_jen_cislice? +Nemá smysl ji psát znovu – funkce int sama nejlíp pozná, co se dá převést na +číslo a co ne. +A dokonce nám to dá vědět – výjimkou, kterou můžeš zachytit.

+

Ono „obsahuje_jen_cislice“ v Pythonu existuje. Dokonce několikrát. +Místo řešení problému to ale spíš ilustruje, v čem problém spočívá:

+
    +
  • Řetězcová metoda isnumeric vrací True pokud řetězec obsahuje číslice: +'123'.isnumeric() je pravda; 'abc'.isnumeric() nepravda. +Problém je, že funkce int potřebuje jeden konkrétní druh číslic: +pro řetězce jako '½' nebo '௩三๓໓' (trojky v tamilském, japonském, +thajském a laoském písmu) platí isnumeric, ale int si na nich +vyláme zuby stejně jako na 'abc'.
  • +
  • Řetězcová metoda isdecimal vrací True pokud řetězec obsahuje arabské +číslice 0-9. To už je lepší, ale stejně to úplně nesedí: int si poradí +s mezerou na začátku, např. s ' 3'. Funkce isdecimal ale takový řetězec +odmítne.
  • +
+

Chceš-li zjistit, jestli funkce int umí daný řetězec převést na číslo, +nejlepší je použít přímo funkci int.

+

Ošetření chyby +# +

+

Pro zachycení chyby má Python příkaz try/except.

+
def nacti_cislo():
+    """Získá od uživatele celé číslo a vrátí ho"""
+    while True:
+        odpoved = input('Zadej číslo: ')
+        try:
+            return int(odpoved)
+        except ValueError:
+            print('To nebylo číslo!')
+            # ... a zeptáme se znovu -- cyklus `while` pokračuje
+

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

+

Druhy chyb +# +

+

A co je to ValueError? To je typ chyby. +Podobných typů je spousta. +Všechny jsou popsané v dokumentaci; pro nás jsou (nebo budou) důležité tyto:

+
BaseException
+ ├── SystemExit                     vyvolána funkcí exit()
+ ├── KeyboardInterrupt              vyvolána po stisknutí Ctrl+C
+ ╰── Exception
+      ├── ArithmeticError
+      │    ╰── ZeroDivisionError    dělení nulou
+      ├── AssertionError            nepovedený příkaz `assert`
+      ├── AttributeError            neexistující atribut/metoda, např. 'abc'.len
+      ├── ImportError               nepovedený import
+      ├── LookupError
+      │    ╰── IndexError           neexistující index, např. 'abc'[999]
+      ├── NameError                 použití neexistujícího jména proměnné
+      │    ╰── UnboundLocalError    použití proměnné, která ještě nebyla nastavená
+      ├── SyntaxError               špatná syntaxe, program je nečitelný/nepoužitelný
+      │    ╰── IndentationError     špatné odsazení
+      │         ╰── TabError        kombinování mezer a tabulátorů v odsazení
+      ├── TypeError                 špatný typ, např. len(9)
+      ╰── ValueError                špatná hodnota, např. int('xyz')
+

Tohle si není potřeba pamatovat – druh chyby, kterou je potřeba zachytit, +vždy najdeš v příslušné chybové hlášce.

+

Když odchytáváš obecnou výjimku, +chytnou se i všechny podřízené typy výjimek – +například except ArithmeticError: zachytí i ZeroDivisionError. +A except Exception: zachytí všechny výjimky, které běžně chceš zachytit.

+

Nechytej je všechny! +# +

+

Většinu chyb není potřeba ošetřovat.

+

Nastane-li nečekaná situace, je téměř vždy +mnohem lepší program ukončit, než se snažit +pokračovat dál a počítat se špatnými hodnotami. +Navíc chybový výstup, který Python standardně +připraví, může hodně ulehčit hledání chyby.

+

Zachytávej tedy jenom ty chyby, které očekáváš – víš přesně, která chyba může +nastat a proč; máš možnost správně zareagovat.

+

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

+

Co ale dělat, když uživatel chce ukončit program a zmáčkne +Ctrl+C? +Nebo když se mu porouchá klávesnice a selže funkce input? +Nejlepší reakce na takovou nečekanou situaci je ukončit program a informovat +uživatele (nebo lépe, programátora), že (a kde) je něco špatně. +Neboli vypsat chybovou hlášku. +A to se stane normálně, bez try.

+

Další přílohy k try +# +

+

Pro úplnost: kromě except existují dva jiné bloky, +které můžeš „přilepit“ k try, a to else a finally. +První se provede, když v try bloku +žádná chyba nenastane; druhý se provede vždy – ať +už chyba nastala nebo ne.

+

Můžeš taky použít více bloků except. Provede se vždy maximálně jeden: +ten první, který danou chybu umí ošetřit.

+
try:
+    neco_udelej()
+except ValueError:
+    print('Tohle se provede, pokud nastane ValueError')
+except NameError:
+    print('Tohle se provede, pokud nastane NameError')
+except Exception:
+    print('Tohle se provede, pokud nastane jiná chyba')
+    # (kromě SystemExit a KeyboardInterrupt, ty chytat nechceme)
+except TypeError:
+    print('Tohle se neprovede nikdy')
+    # ("except Exception" výše ošetřuje i TypeError; sem se Python nedostane)
+else:
+    print('Tohle se provede, pokud chyba nenastane')
+finally:
+    print('Tohle se provede vždycky; i pokud v `try` bloku byl např. `return`')
+

Vyvolání chyby +# +

+

Občas se stane, že výjimku budeš potřebovat vyvolat sám/sama.

+

Často se to stává, když píšeš nějakou obecnou funkci. +Třeba funkci na výpočet obsahu čtverce. +Co se stane, když někdo zavolá obsah_ctverce(-5)?

+
    +
  • Zadal-li ono -5 uživatel, je potřeba mu vynadat a zeptat se znovu.
  • +
  • Naměřil-li -5 nějaký robotický aparát, je potřeba ho líp zkalibrovat.
  • +
  • Vyšel-li čtverec se stranou -5 v nějakém výpočtu, je nejspíš potřeba opravit +chybu v tom výpočtu.
  • +
+

Samotná funkce obsah_ctverce ale „neví“, proč ji někdo volá. +Jejím úkolem je jen něco spočítat. +Měla by být použitelná ve všech případech výše – a v mnoha dalších.

+

Když někdo zavolá obsah_ctverce(-5), neexistuje správný výsledek, který by +funkce mohla vrátit. +Místo vrácení výsledku musí tato funkce signalizovat chybu. +S tou se pak může program, který obsah_ctverce(-5) zavolal, +vypořádat – vynadat uživateli, zkalibrovat měřák, nebo, pokud na chybu není +připravený, sám skončit s chybou a upozornit tak programátora, že je něco +špatně.

+

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

+
def obsah_ctverce(strana):
+    """Vrátí obsah čtverce s danou délkou strany"""
+    if strana > 0:
+        return strana ** 2
+    else:
+        raise ValueError(f'Strana musí být kladná, číslo {strana} kladné není!')
+

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

+

Ze začátku není u raise příliš důležité dumat nad tím, který typ výjimky je +ten správný. +Klidně „střílej od boku“. +ValueError bývá často správná volba.

\ No newline at end of file diff --git a/lessons/expressions/index.html b/lessons/expressions/index.html new file mode 100644 index 00000000..ebe5dfcc --- /dev/null +++ b/lessons/expressions/index.html @@ -0,0 +1,73 @@ +

Vyhodnocování výrazů +# +

+

Už víš, že Python se dá použít jako kalkulačka: dokáže spočítat +hodnotu výrazu (angl. expression) jako 3 * (5 + 2). +Jak to ale vlastně dělá? +Jak se vyhodnocují výrazy?

+

Pro základní výrazy je to tak, jak to možná znáš ze školy. +U 3 * (5 + 2) nejdřív spočítáš to, co je v závorkách: (5 + 2) je 7. +Výsledek dosadíš do původního výrazu místo závorky: 3 * 7. +Stejně fungují výrazy v Pythonu.

+

Možná to zní jednoduše, ale protože budeme ten samý postup používat +i na složitější výrazy, hodí se ho umět „rozepsat“:

+
vysledek = 3 * (5 + 2)
+#              ╰──┬──╯
+vysledek = 3 *    7
+#          ╰─┬────╯
+vysledek =  21
+

Když Python potřebuje vyhodnotit proměnnou, dosadí její hodnotu. +Pokud je zrovna v proměnné a číslo 4, za a se dosadí 4:

+
a = 4
+b = 5
+
+vysledek = (a + b) / a
+#           |   |    |
+vysledek = (4 + 5) / 4
+#          ╰──┬──╯
+vysledek =    9    / 4
+#             ╰────┬─╯
+vysledek =        2.25
+

Funguje to i u složitých výrazů. +Python se složitými výrazy nemá problém. +Jen člověk, který program čte či píše, se v nich může lehce ztratit. +Když opravdu potřebuješ napsat složitý výraz, je dobré jej rozdělit na několik +menších nebo vysvětlit pomocí komentáře.

+

Je ale dobré mít povědomí o tom, jak složité výrazy „fungují“, +aby ses jich nemusel/a bát. +Měl/a bys být schopný/á vysvětlit, co se stane, +když se Pythonu zeptáš, kolik je -b + (b² + +4ac)⁰·⁵ / (2a), abys pak věděl/a, co za +tebe Python dělá.

+
a = 2
+b = 5
+c = 3
+
+
+x = -b + (b ** 2 + 4 * a * c) ** 0.5 / (2 * a)
+#    |    |            |   |                |
+x = -5 + (5 ** 2 + 4 * 2 * 3) ** 0.5 / (2 * 2)
+#         ╰──┬─╯   ╰─┬─╯               ╰──┬──╯
+x = -5 + (  25   +   8   * 3) ** 0.5 /    4
+#                   ╰────┬─╯
+x = -5 + (  25   +      24  ) ** 0.5 /    4
+#        ╰───────┬──────────╯
+x = -5 +         49           ** 0.5 /    4
+#                ╰──────┬──────────╯
+x = -5 +               7.0           /    4
+#                      ╰─────────────┬────╯
+x = -5 +                            1.75
+#   ╰──────────────┬───────────────────╯
+x =              -3.25
+

Výrazy se používají na více místech Pythonu než jen v přiřazování +do proměnných. +Třeba podmínka u if je taky výraz a vyhodnocuje se stejně jako ostatní +výrazy:

+
strana = -5
+
+if strana <= 0:
+    print("Strana musí být kladná!")
+
if strana <= 0:
+#  ╰──────┬──╯
+if      True  :
+
\ No newline at end of file diff --git a/lessons/fast-track/http/index.md b/lessons/fast-track/http/index.md deleted file mode 100644 index 82bf3094..00000000 --- a/lessons/fast-track/http/index.md +++ /dev/null @@ -1,182 +0,0 @@ -# HTTP – Jak funguje Internet - -Než začneme pracovat s internetem – ať už tvorbou vlastních stránek, nebo -komunikací s existujícími službami, pojďme si přiblížit, co vlastně ten -internet je a jak funguje. - -Internet je celosvětová síť počítačů. -Je to spousta laptopů, stolních počítačů, malých blikajících krabiček -i obrovských blikajících skříní, které jsou navzájem propojeny pomocí -kabelů (nebo i bezdrátově). - -Samozřejmě není každé zařízení propojené s každým jiným zařízením – tolik -kabelů by se na Zemi těžko vešlo. -Spousta zařízení – hlavně tzv. *routery* a *switche* – ale umí přeposílat -zprávy mezi sebou tak, že každý počítač může komunikovat s každým -jiným počítačem. -(Aspoň teoreticky – reálně je komunikace omezená např. kvůli bezpečnosti.) - -Funguje to podobně jako pošta: když pošlu balíček z Brna do Melbourne, -nedostane se tam přímo. -Balíček poputuje třeba vlakem do Prahy, pak letadlem do hlavní pošty -v Austrálii a odtud náklaďákem do Melbourne, kde ho doručovatel donese až -k domu příjemce. -A k naplánování celé téhle cesty stačí napsat na obálku krátkou adresu. - -Podobně cestují informace v internetu: z laptopu přes Wi-Fi do *routeru*, -odtud kabelem k poskytovateli připojení, tlustším kabelem do české -„páteřní sítě“, podmořským kabelem třeba do Ameriky… a nakonec k počítači, -se kterým jsem chtěl komunikovat. - -Většinou můj laptop takhle komunikuje se *serverem*, počítačem, který -se stará o sdělování informací. -Každou webovou stránku spravuje takový server. - -{{ anchor('url-anatomy') }} -## Webové adresy - -Jak taková komunikace vypadá si ukážeme na příkladu – -co se stane, když do prohlížeče zadám tuhle adresu: - -```plain -http://naucse.python.cz/lessons/fast-track/http/ -``` - -Taková webová adresa – technicky zvaná URL (*Uniform Resource Locator*, -„jednotná adresa zdroje“) přesně určuje, jak se má prohlížeč dostat -k informacím, které má zobrazit. - -{{ figure( - img=static('url-anatomy.svg'), - alt='http://naucse.python.cz/lessons/fast-track/http/' -) }} - -Začátek adresy, `http://`, je jméno *protokolu* (angl. *protocol name*). -Protokol určuje způsob, *jak* se k daným informacím dostat. -Protokolů existuje spousta, každý funguje trochu jinak a každý se používá -na něco jiného: -SMTP a POP pro e-mail, FTP pro přenos souborů, SSH pro ovládání počítačů. -My se teď ale zaměříme na HTTP, který se typicky používá pro webové stránky. - - -Další část adresy, `naucse.python.cz`, je *jméno serveru* (angl. *server name*). -Říká, *kde* prohlížeč najde dané informace. - -Jméno serveru je jako poštovní adresa – existuje počítač, který se jmenuje -`naucse.python.cz`, a každý internetový „pošťák“ ví, komu přeposlat zprávu, -aby se k tomuto počítači nakonec dostala. - -> [note] -> „Skutečná“ adresa počítače, tzv. IP adresa, je číselná – například -> `151.101.37.147` nebo `2a04:4e42:9::403`. -> Existuje ale systém, jak jméno serveru na takovou *IP adresu* přeložit. -> Tenhle systém se jmenuje DNS a – abychom zůstali u přirovnání k poště – -> funguje podobně jako seznamy poštovních směrovacích čísel. - - -Poslední část URL, `/lessons/fast-track/http/`, je *cesta* (angl. *path*). -Říká, *co* chceme od serveru dostat: jméno konkrétní webové stránky. - -U jednodušších stránek to může být přímo jméno souboru, který má server -uložený na disku – proto spousta adres na Webu končí příponou `.html`. - - -## Požadavek a odpověď - -K získání požadované stránky prohlížeč vytvoří *požadavek* (angl. *request*) -– zprávu „Pošli mi prosím stránku `/lessons/fast-track/http/`“ – a pošle ho -serveru `naucse.python.cz`. - -Server požadavek dostane a vrátí *odpověď* (angl. *response*) – zprávu -s obsahem dané stránky. -Obsah je často webová stránka v jazyce HTML, který popisuje co na stránce je, -kde jsou nadpisy a kde odstavce, jak má stránka vypadat, a tak dále. -Ale v odpovědi může být místo stránky i cokoli jiného – obrázek, video, nebo -jiná data. - -Veškerá komunikace přes HTTP funguje právě takto: pošle se požadavek -a přijde na něj odpověď. - -A jak tyhle zprávy vypadají? -Požadavek nějak takhle: - -```http -GET /lessons/fast-track/install/ HTTP/1.1 -Accept: */* -Accept-Encoding: gzip, deflate -Connection: keep-alive -Host: naucse.python.cz -User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0 -``` - -První řádek říká serveru: prosím, pošli mi (`GET`) stránku -`/lessons/fast-track/install/` s použitím protokolu `HTTP` verze `1.1`. -Další řádky jsou *hlavičky* (angl. *headers*). -Říkají například kdo se ptá (`User-Agent`) a jaký obsah očekává (`Accept`). -Většina hlaviček je nepovinná. - -Odpověď pak může vypadat takto: - -```http -HTTP/1.1 200 OK -Cache-Control: max-age=600 -Connection: keep-alive -Content-Encoding: gzip -Content-Length: 3127 -Content-Type: text/html; charset=utf-8 -Date: Tue, 20 Feb 2018 15:51:24 GMT -Last-Modified: Tue, 20 Feb 2018 15:20:08 GMT -Server: GitHub.com - - - - - - - - Python a jeho knihovny: HTTP – Jak funguje Internet -… -``` - -První řádek říká: používáme protokol `HTTP` verze `1.1`, -a všechno je v pořádku (`200 OK`). -Kromě `200` existují i další [stavové kódy] (angl. *status codes*). -Známý je např. `404` „nenalezeno“. - -[stavové kódy]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes - -Následují opět hlavičky – např. kdo odpovídá (`Server`), kdy byla stránka -naposledy změněna (`Last-Modified`) a jak je odpověď zakódovaná: -`Content-Type: text/html` říká, že je to stránka v jazyce HTML. - -Hlavičky jsou ukončené volným řádkem, po kterém následuje samotný obsah -odpovědi ve zmíněném jazyce HTML. - - -## HTTP Metody - -Komunikace ukázaná výše používala metodu `GET`, která slouží ke *čtení* -informací. -Když se takto prohlížeč na nějakou stránku zeptá, nic se na serveru nezmění. -Prohlížeč si takovou stránku – nebo třeba obrázek či video – může dočasně -uložit, a když bude potřeba znovu, použít uloženou verzi. - -Některými požadavky ale stav serveru mění: například se přihlásí uživatel, -nakoupí zboží v e-shopu nebo odešle zpráva do diskuse. -Tyto požadavky používají místo `GET` jinou *metodu* (angl. *method*). -Co přesně která metoda na jaké adrese dělá, to záleží na autorovi stránek. -Často se používají tyto metody: - -* `GET` načte informace, -* `POST` pošle na server informace, např. z formuláře, s cílem něco - změnit nebo nastavit, -* `PUT` přidá novou stránku (nebo jiný objekt), -* `DELETE` něco smaže. - -Seznam všech metod je ve -[specifikaci](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html). - -U složitějších požadavků se dají na server poslat i informace: -webové formuláře se odesílají požadavkem, který používá metodu `POST` -a vyplněné informace k dotazu „přilepí“ za hlavičky – stejným způsobem, jako se -v odpovědi posílá HTML stránka. diff --git a/lessons/fast-track/http/info.yml b/lessons/fast-track/http/info.yml deleted file mode 100644 index cd3d2389..00000000 --- a/lessons/fast-track/http/info.yml +++ /dev/null @@ -1,6 +0,0 @@ -title: HTTP – Jak funguje Internet -style: md -attribution: - - Pro naucse.python.cz napsal Petr Viktorin, 2018. - - Inspirováno tutoriálem [Django Girls](https://tutorial.djangogirls.org/en/how_the_internet_works/) -license: cc-by-sa-40 diff --git a/lessons/fast-track/install/index.md b/lessons/fast-track/install/index.md deleted file mode 100644 index 7bf759c1..00000000 --- a/lessons/fast-track/install/index.md +++ /dev/null @@ -1,87 +0,0 @@ -V tomto kurzu budeme používat *virtuální prostředí*. -Jedná se o oddělené prostředí pro Python, kam se dají instalovat jednotlivé -knihovny, které jsou potom aktivní jen uvnitř. -Použití prostředí má dvě hlavní výhody: - -* Je-li prostředí aktivované, - příkaz `python` spustí verzi Pythonu, se kterou bylo prostředí nainstalováno. - Takže pak např. v materiálech nemusíme mít speciální instrukce pro - Linux (`python3`) a Windows (`py -3`). - -* Instalace knihoven nezasahuje do systémového nastavení, ani do jiných - virtuálních prostředí. - Můžeš tak oddělit jednotlivé projekty; v každém prostředí můžou být - nainstalované jiné verze knihoven. - A když se něco pokazí, adresář s virtuálním prostředím můžeš prostě smazat - a vytvořit znovu. - -Následují zrychlené instrukce; předpokládám, že Python (3.6 a vyšší) -už máš nainstalovaný a že znáš základy práce -s [příkazovou řádkou]({{ lesson_url('beginners/cmdline') }}). - -Podrobný postup instalace Pythonu a vytvoření prostředí je je popsán -v příslušné [lekci pro začátečníky]({{ lesson_url('beginners/install') }}). -Modul `venv` je součást [standardní knihovny](https://docs.python.org/3/library/venv.html). - -## Unix (Linux, macOS) - -Zkontroluj, že máš modul `ensurepip`: - -```console -$ python3.7 -m ensurepip --version -pip 18.0 (nebo i jiná verze) -``` - -Jestli ne, postupuj podle [lekce pro začátečníky]({{ lesson_url('beginners/install') }}) – -jsou tam podrobnější instrukce. -Jinak použij: - -```console -$ mkdir project -$ cd project -$ python3.7 -m venv __venv__ # vytvoření virtualenvu -- použij Python 3 dle systému -$ . __venv__/bin/activate # aktivace -(__venv__)$ python -m pip install requests # příkaz na instalaci balíčků puštěný ve virtualenvu -(__venv__)$ ... # práce "uvnitř" -(__venv__)$ deactivate # vypnutí virtualenvu -``` - -{% if var('mi-pyt') %} -V tomto kurzu lze případně využít i Python 3.6. Pokud máš 3.5 nebo ještě nižší, -doporučujeme aktualizovat. -{% endif %} - -## Windows - -```dosvenv -> mkdir project -> cd project -> py -3 -m venv __venv__ -> __venv__\Scripts\activate -(__venv__)> python -m pip install requests # příkaz na instalaci balíčků puštěný ve virtualenvu -(__venv__)> ... # práce "uvnitř" -(__venv__)> deactivate # vypnutí virtualenvu -``` - -## Poznámky - -Příkaz `. __venv__/bin/activate` budeš muset zadat vždy, než začneš na projektu -pracovat. - -Ono `__venv__` je jen jméno adresáře. Můžeš si ho pojmenovat jak chceš; dokonce -nemusí být v rámci adresáře s projektem. -Někteří lidé mají všechny virtuální prostředí na jednom místě; dokonce existuje -nástroj [virtualenvwrapper] na správu takového řešení, případně [pipenv], -který _Python Packaging Authority_ [doporučuje] pro vývoj aplikací (my ale -budeme vyvíjet i knihovny). - -Autoři tohoto textu tedy doporučují `__venv__` v adresáři s projektem. -Při použití v kombinaci s Gitem nezapomeň tento adresář přidat do souboru -`.gitignore`. - -[virtualenvwrapper]: https://virtualenvwrapper.readthedocs.io/en/latest/ -[pipenv]: https://pipenv.readthedocs.io/en/latest/ -[doporučuje]: https://packaging.python.org/tutorials/managing-dependencies/ - -Příkaz `python -m pip install` nainstaluje danou knihovnu – může to být i jiná -než `requests` jako výše. diff --git a/lessons/fast-track/install/info.yml b/lessons/fast-track/install/info.yml deleted file mode 100644 index f362fe13..00000000 --- a/lessons/fast-track/install/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Vytvoření virtuáního prostředí -style: md -attribution: Pro kurz MI-PYT na ČVUT napsali Miro Hrončok a Petr Viktorin, 2016-2017. -license: cc-by-sa-40 diff --git a/lessons/fast-track/yaml/index.md b/lessons/fast-track/yaml/index.md deleted file mode 100644 index 26302e0d..00000000 --- a/lessons/fast-track/yaml/index.md +++ /dev/null @@ -1,40 +0,0 @@ - -# O formátu YAML - -Formát se skládá z několika stavebních bloků, které se dají poté skládat dohromady. -Prvním je seznam hodnot (podobně jako v Pythonu). -Seznam se ve formátu YAML zapisuje následovně: - -```yaml -- První položka -- Druhá položka -- 3 -- Položka může být i něco jiného než text! -``` - -Druhým stavebním blokem je slovník (zase podobně jako v Pythonu), který se skládá z klíčů a hodnot, nezáleží v ní tedy na pořadí, protože se k informaci vždy člověk dostance pomocí klíče. -Slovník se ve formátu YAML zapisuje následovně: - -```yaml -klic1: Hodnota klíče klic1. -klic2: 2 -klic3: I hodnoty ve slovníku mohou být něco jiného než text. -klic4: | - Pokud potřebuješ zapsat nějaký delší text, uděláš to takhle. - - Tento text budou dva separátní odstavce v rámci jednoho klíče. -klic5: -klic6: Jak vidíš v klíči klic5, hodnota může být i prázdá. -``` - -Seznamy a slovníky jde samozřejmě i skládat dohromady: - -```yaml -klic1: Hodnota klíče -klic2: -- Tohle je seznam, které patří pod klíč klic2 -- vnorenyklic: Můžeme skládat jednotlivé typy skoro do nekonečna. -- vnorenyklic: Klíče musí být unikátní jen v rámci jednoho slovníku, takže to může být takto. -klic3: - vnorenyklic: Slovník může obsahovat další slovník i takto. -``` diff --git a/lessons/fast-track/yaml/info.yml b/lessons/fast-track/yaml/info.yml deleted file mode 100644 index 0841bbd3..00000000 --- a/lessons/fast-track/yaml/info.yml +++ /dev/null @@ -1,6 +0,0 @@ -title: O formátu YAML -style: md -attribution: - - Napsal Mikuláš Poul, 2018 -license: cc-by-sa-40 -license_code: cc0 diff --git a/lessons/files/index.html b/lessons/files/index.html new file mode 100644 index 00000000..2dc93387 --- /dev/null +++ b/lessons/files/index.html @@ -0,0 +1,136 @@ +<h1 id="soubory">Soubory +<a href="#soubory" class="header-link">#</a> +</h1> +<p>Dnes se podíváme na to, jak v Pythonu číst z +(a pak i zapisovat do) souborů.</p> +<p>Vytvoř si v editoru soubor <code>basnicka.txt</code> a napiš do něj libovolnou básničku. +Soubor ulož.</p> +<div class="admonition note"><p>Na uložení souboru s básničkou doporučuji použít +stejný editor, jaký používáš na Pythonní programy.</p> +<p>Používáš-li jiný editor než Atom, dej si při ukládání pozor na kódování:</p> +<ul> +<li>Nabízí-li ti editor při ukládání výběr kódování, vyber UTF-8.</li> +<li>Je-li k dispozici kódování „UTF-8 bez BOM”, použij to.</li> +<li>Pokud musíš použít Notepad, který výše uvedené možnosti nemá, pak v kódu +níže použij místo <code>'utf-8'</code> nestandardní <code>'utf-8-sig'</code>.</li> +</ul> +<p>Ono <a href="https://en.wikipedia.org/wiki/UTF-8"><code>utf-8</code></a> je název standardního kódování. +Zajišťuje, že se případné emoji nebo znaky s diakritikou do souboru uloží +tak, aby se daly přečíst i na jiném počítači či operačním systému. +🎉</p> +</div><p>Potom napiš tento program:</p> +<div class="highlight"><pre><span></span><span class="n">soubor</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'basnicka.txt'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span> +<span class="n">obsah</span> <span class="o">=</span> <span class="n">soubor</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> +<span class="n">soubor</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> + +<span class="nb">print</span><span class="p">(</span><span class="n">obsah</span><span class="p">)</span> +</pre></div><p>a spusť ho z adresáře, ve kterém je +<code>basnicka.txt</code> (jinými slovy, aktuální adresář musí být ten, který +obsahuje soubor s básničkou).</p> +<p>Obsah souboru se vypíše!</p> +<p>Co se tu děje? +Tak jako <code>int()</code> vrací čísla a <code>input()</code> řetězce, funkce +<code>open()</code> vrací hodnotu, která představuje <em>otevřený soubor</em>. +Tahle hodnota má vlastní metody. +Tady používáme metodu <code>read()</code>, která +najednou přečte celý obsah souboru a vrátí ho jako řetězec. +Nakonec metoda <code>close()</code> otevřený soubor zase zavře.</p> +<h2 id="automaticke_zavirani_souboru">Automatické zavírání souborů +<a href="#automaticke_zavirani_souboru" class="header-link">#</a> +</h2> +<p>Soubory se dají přirovnat k ledničce: abys něco +mohl/a z ledničky vzít, nebo dát dovnitř, musíš +ji předtím otevřít a potom zavřít. +Bez zavření to sice na první pohled funguje taky, +ale pravděpodobně potom brzo něco zplesniví.</p> +<p>Stejně tak je docela důležité soubor zavřít po tom, +co s ním přestaneš pracovat. +Bez zavření to na první pohled funguje, ale složitější programy se můžou dostat +do problémů. +Operační systémy mají limity na počet +současně otevřených souborů, které se nezavíráním +dají snadno překročit. +Na Windows navíc nemůžeš soubor, který je stále +otevřený, otevřít znovu.</p> +<p>Na korektní zavření souboru ale programátoři často zapomenou. +Proto Python poskytuje příkaz <code>with</code>, který soubory zavírá automaticky. +Používá se takhle:</p> +<div class="highlight"><pre><span></span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'basnicka.txt'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span> <span class="k">as</span> <span class="n">soubor</span><span class="p">:</span> + <span class="n">obsah</span> <span class="o">=</span> <span class="n">soubor</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> + +<span class="nb">print</span><span class="p">(</span><span class="n">obsah</span><span class="p">)</span> +</pre></div><p>Příkaz <code>with</code> vezme otevřený soubor (který vrací funkce <code>open</code>) +a přiřadí ho do proměnné <code>soubor</code>. +Pak následuje odsazený blok kódu, kde se souborem můžeš pracovat – v tomhle +případě pomocí metody <code>read</code> přečíst obsah jako řetězec. +Když se Python dostane na konec odsazeného bloku, soubor automaticky zavře.</p> +<p>V naprosté většině případů je pro otevírání souborů nejlepší použít <code>with</code>.</p> +<h2 id="iterace_nad_soubory">Iterace nad soubory +<a href="#iterace_nad_soubory" class="header-link">#</a> +</h2> +<p>Otevřené soubory se, jako např. řetězce či <code>range</code>, +dají použít s příkazem <code>for</code>. +Tak jako <code>for i in range</code> poskytuje za sebou jdoucí čísla a <code>for c in 'abcd'</code> +poskytuje jednotlivé znaky řetězce, <code>for radek in soubor</code> bude do proměnné +<code>radek</code> dávat jednotlivé řádky čtené ze souboru.</p> +<p>Například můžeš básničku odsadit, +aby se vyjímala v textu:</p> +<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s1">'Slyšela jsem tuto básničku:'</span><span class="p">)</span> +<span class="nb">print</span><span class="p">()</span> + +<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'basnicka.txt'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span> <span class="k">as</span> <span class="n">soubor</span><span class="p">:</span> + <span class="k">for</span> <span class="n">radek</span> <span class="ow">in</span> <span class="n">soubor</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">' '</span> <span class="o">+</span> <span class="n">radek</span><span class="p">)</span> + +<span class="nb">print</span><span class="p">()</span> +<span class="nb">print</span><span class="p">(</span><span class="s1">'Jak se ti líbí?'</span><span class="p">)</span> +</pre></div><p>Když to zkusíš, zjistíš, že trochu nesedí +řádkování. Zkusíš vysvětlit, proč tomu tak je?</p> +<div class="solution" id="solution-0"> + <h3>Řešení</h3> + <div class="solution-cover"> + <a href="naucse:solution?solution=0"><span class="link-text">Ukázat řešení</span></a> + </div> + <div class="solution-body" aria-hidden="true"> + <p>Každý řádek končí znakem nového řádku, <code>'\n'</code>, +<span></span>který možná znáš ze <a href="naucse:page?lesson=beginners/str">sekce o řetězcích</a>. +<span></span>Při procházení souboru Python tento znak nechává na konci řetězce <code>radek</code> ¹. +<span></span>Funkce <code>print</code> pak přidá další nový řádek, protože ta na konci +<span></span>výpisu vždycky odřádkovává – pokud nedostane argument <code>end=''</code>.</p> +<span></span><hr /> +<span></span><p>¹ Proč to dělá? Kdyby <code>'\n'</code> na konci řádků nebylo, +<span></span>nedalo by se např. dobře rozlišit, jestli poslední řádek +<span></span>končí na <code>'\n'</code></p> + </div> +</div> + +<p>Ideální způsob, jak odřádkování spravit, je odstranit z konce řetězce +bílé znaky (mezery a nové řádky) pomocí metody <code>rstrip</code>:</p> +<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s1">'Slyšela jsem tuto básničku:'</span><span class="p">)</span> +<span class="nb">print</span><span class="p">()</span> + +<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'basnicka.txt'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span> <span class="k">as</span> <span class="n">soubor</span><span class="p">:</span> + <span class="k">for</span> <span class="n">radek</span> <span class="ow">in</span> <span class="n">soubor</span><span class="p">:</span> + <span class="n">radek</span> <span class="o">=</span> <span class="n">radek</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">' '</span> <span class="o">+</span> <span class="n">radek</span><span class="p">)</span> + +<span class="nb">print</span><span class="p">()</span> +<span class="nb">print</span><span class="p">(</span><span class="s1">'Jak se ti líbí?'</span><span class="p">)</span> +</pre></div><h2 id="psani_souboru">Psaní souborů +<a href="#psani_souboru" class="header-link">#</a> +</h2> +<div class="admonition warning"><p class="admonition-title">Pozor!</p> +<p>Pro Python není problém smazat obsah jakéhokoli souboru. +Psaní do souborů si zkoušej v adresáři, ve kterém nemáš uložené +důležité informace!</p> +</div><p>Soubory se v Pythonu dají i zapisovat. +Pro zápis soubor otevři s pojmenovaným +argumentem <code>mode='w'</code> (z angl. <em>mode</em>, mód a <em>write</em>, psát).</p> +<p>Pokud soubor už existuje, otevřením s <code>mode='w'</code> se veškerý jeho obsah smaže. +Po zavření tak v souboru bude jen to, co do něj ve svém programu zapíšeš.</p> +<p>Informace pak do souboru zapiš známou funkcí <code>print</code>, +a to s pojmenovaným argumentem <code>file</code>:</p> +<div class="highlight"><pre><span></span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'druha-basnicka.txt'</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s1">'w'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span> <span class="k">as</span> <span class="n">soubor</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'Naše staré hodiny'</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">soubor</span><span class="p">)</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'Bijí'</span><span class="p">,</span> <span class="mi">2</span><span class="o">+</span><span class="mi">2</span><span class="p">,</span> <span class="s1">'hodiny'</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">soubor</span><span class="p">)</span> +</pre></div> \ No newline at end of file diff --git a/lessons/first-steps/index.html b/lessons/first-steps/index.html new file mode 100644 index 00000000..d84e4f62 --- /dev/null +++ b/lessons/first-steps/index.html @@ -0,0 +1,91 @@ +<h1 id="interaktivni_rezim_pythonu">Interaktivní režim Pythonu +<a href="#interaktivni_rezim_pythonu" class="header-link">#</a> +</h1> +<p>Chceš-li si začít hrát s Pythonem, otevři <em>příkazový řádek</em> a aktivuj virtuální prostředí. +Zkontroluj si, že ti na začátku příkazové řádky svítí <code>(venv)</code>.</p> +<p>Je-li tomu tak, nezbývá než – konečně – pustit Python. K tomu použij příkaz <code>python</code>:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>python +<span class="go">Python 3.6.6 (...)</span> +<span class="go">Type "help", "copyright", "credits" or "license" for more information.</span> +<span class="go">>>></span> +</pre></div><p>Příkaz vypíše několik informací. Z prvního řádku se můžeš ujistit, že používáš Python 3. +(Verze by měla být <code>3.6</code> nebo vyšší. Vidíš-li číslo jako <code>2.7.11</code>, něco je špatně – popros o radu kouče.) +Další řádek je informační: Python má k dispozici návody a informace sám o sobě, +ale jsou psané v angličtině a pro trochu pokročilejší publikum.</p> +<p>Třemi „zobáčky“ <code>>>></code> pak Python poprosí o instrukce. +Je to jako v příkazové řádce, ale místo příkazů jako <code>cd</code> a <code>mkdir</code> sem budeš psát příkazy Pythonu.</p> +<p>Příkazy z příkazové řádky v Pythonu nefungují, +ačkoli okýnko vypadá skoro stejně. +Vyzkoušej si to. Za „zobáčky“ napiš <code>whoami</code> a zmáčkni Enter:</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">whoami</span> +<span class="gt">Traceback (most recent call last):</span> + File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></span> +<span class="gr">NameError</span>: <span class="n">name 'whoami' is not defined</span> +</pre></div><p>Tohle je <em>chybová hláška</em>, která se objeví vždycky, +když Python nebude spokojený. +V průběhu kurzu jich uvidíš ještě spoustu, +takže si ji dobře prohlédni, ať ji příště poznáš.</p> +<h2 id="prvni_prikaz">První příkaz +<a href="#prvni_prikaz" class="header-link">#</a> +</h2> +<p>Třemi „zobáčky“ <code>>>></code> Python prosí o instrukce. +Pojď mu nějakou dát!</p> +<p>Ze začátku použij Pythonu jako kalkulačku. +Za tři zobáčky napiš třeba <code>2 + 3</code> a zmáčkni <kbd>Enter</kbd>.</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="mi">2</span> <span class="o">+</span> <span class="mi">3</span> +<span class="go">5</span> +</pre></div><div class="admonition note"><p>Zobáčky <code>>>></code> i odpověď vypisuje sám Python! +sám/sama zadej jen číslo a Enter.</p> +</div><p>Zobrazila se ti správná odpověď? +Pokud ano, gratuluji! První příkaz v Pythonu máš za sebou.</p> +<p>Zkusíš i odečítání?</p> +<p>A jak je to s násobením? +Na kalkulačce bys zadal/a <code>4 × 5</code>, což se na klávesnici píše špatně. +Python proto používá symbol <code>*</code>.</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="mi">4</span> <span class="o">*</span> <span class="mi">5</span> +<span class="go">20</span> +</pre></div><p>Symboly jako <code>+</code> a <code>*</code> se odborně nazývají <em>operátory</em>.</p> +<p>Operátor pro dělení je <code>/</code> – jako u násobení, znak <code>÷</code> by se psal špatně.</p> +<p>Při dělení může vzniknout necelé číslo, třeba dva a půl. +Python používá desetinnou <em>tečku</em>, ukáže se tedy <code>2.5</code>:</p> +<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">2</span> +<span class="mf">2.5</span> +</pre></div><p>Z důvodů, do kterých teď nebudeme zabíhat, se při dělení desetinná tečka +objeví, i když vyjde číslo celé:</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="mi">4</span> <span class="o">/</span> <span class="mi">2</span> +<span class="go">2.0</span> +</pre></div><p>Občas se hodí použít dělení se zbytkem, kdy výsledek zůstane jako celé číslo. +Na to má Python operátory <code>//</code> (podíl) a <code>%</code> (zbytek):</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="mi">5</span> <span class="o">//</span> <span class="mi">2</span> +<span class="go">2</span> +<span class="gp">>>> </span><span class="mi">5</span> <span class="o">%</span> <span class="mi">2</span> +<span class="go">1</span> +</pre></div><div class="admonition style-note"><p>Mezery mezi čísly a znamínkem nejsou nutné: <code>4*5</code> i <code>4 * 5</code> dělá +to samé co <code>4 * 5</code>. +Je ale zvykem psát kolem operátoru jednu mezeru z každé strany – tak jako +v těchto materiálech. +Kód je pak čitelnější.</p> +</div><h3 id="ukonceni">Ukončení +<a href="#ukonceni" class="header-link">#</a> +</h3> +<p>Pokud ses dostal/a až sem, gratuluji! +Python máš nejen nainstalovaný, ale taky ti funguje. +Stačí ho už jen zavřít. +V Pythonu se to dělá pomocí <code>quit()</code>, s prázdnými závorkami na konci.</p> +<div class="highlight"><pre> +<span class="gp">>>></span> quit() +<span class="gp">(venv)$</span> +</pre></div> + +<p>Zobáčky <code>>>></code> se změnily na výzvu +příkazové řádky, která začíná <code>(venv)</code> a končí <code>$</code> nebo <code>></code>. +Teď fungují příkazy jako <code>whoami</code> a <code>cd</code>, ale příkazy Pythonu +jako <code>1 + 2</code> fungovat nebudou, dokud Python opět nepustíš pomocí +příkazu <code>python</code>.</p> +<p>Ukončit virtuální prostředí můžeš příkazem <code>deactivate</code> – +tentokrát bez závorek.</p> +<div class="highlight"><pre><span></span><span class="gp gp-VirtualEnv">(venv)</span><span class="gp">$ </span>deactivate +</pre></div><p>Příkazovou řádku můžeš nakonec zavřít příkazem <code>exit</code>.</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span><span class="nb">exit</span> +</pre></div><p>Pro cvik si zkus Python znovu spustit – nejdřív otevři příkazovou řádku, +pak aktivuj virtuální prostředí, potom spusť Python samotný.</p> \ No newline at end of file diff --git a/lessons/flask/index.html b/lessons/flask/index.html new file mode 100644 index 00000000..1c37ba22 --- /dev/null +++ b/lessons/flask/index.html @@ -0,0 +1,395 @@ +<h1 id="webove_aplikace_flask">Webové aplikace: Flask +<a href="#webove_aplikace_flask" class="header-link">#</a> +</h1> +<p>Python je víceúčelový jazyk. +Na minulém cvičení jsme tvořili aplikace pro příkazovou řádku, +nyní se podíváme na aplikace webové.</p> +<p>Webových frameworků pro Python je více, mezi nejznámější patří <a href="https://www.djangoproject.com/">Django</a>, +<a href="https://flask.palletsprojects.com">Flask</a> nebo <a href="http://www.pylonsproject.org/">Pyramid</a>.</p> +<p>Pro naše účely použijeme <a href="https://flask.palletsprojects.com">Flask</a>, protože je nejrychlejší na pochopení a +nevyžaduje striktně použití <a href="https://cs.wikipedia.org/wiki/Model-view-controller">MVC</a> paradigmatu.</p> +<h2 id="flask">Flask +<a href="#flask" class="header-link">#</a> +</h2> +<p>Flask opět můžete nainstalovat do virtualenvu, nejlépe použít projekt +z minulého cvičení:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span><span class="nb">cd</span><span class="w"> </span>project +<span class="gp">$ </span>.<span class="w"> </span>__venv__/bin/activate<span class="w"> </span> +<span class="gp gp-VirtualEnv">(__venv__)</span> <span class="gp">$ </span>python<span class="w"> </span>-m<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>Flask +</pre></div><p>Základní použití Flasku je poměrně primitivní. +Do souboru <code>hello.py</code> napište:</p> +<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> +<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span> + +<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">index</span><span class="p">():</span> + <span class="k">return</span> <span class="s1">'MI-PYT je nejlepší předmět na FITu!'</span> +</pre></div><p>Pak aplikaci spusťte pomocí následujících příkazů. +(Na Windows použijte místo <code>export</code> příkaz <code>set</code>.)</p> +<div class="highlight"><pre><span></span><span class="gp gp-VirtualEnv">(__venv__)</span> <span class="gp">$ </span><span class="nb">export</span><span class="w"> </span><span class="nv">FLASK_APP</span><span class="o">=</span>hello.py +<span class="gp gp-VirtualEnv">(__venv__)</span> <span class="gp">$ </span><span class="nb">export</span><span class="w"> </span><span class="nv">FLASK_DEBUG</span><span class="o">=</span><span class="m">1</span> +<span class="gp gp-VirtualEnv">(__venv__)</span> <span class="gp">$ </span>flask<span class="w"> </span>run +<span class="go"> * Serving Flask app "hello"</span> +<span class="go"> * Forcing debug mode on</span> +<span class="go"> * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)</span> +<span class="go"> * Restarting with stat</span> +<span class="go"> * Debugger is active!</span> +<span class="go"> * Debugger PIN: 189-972-345</span> +</pre></div><p>Na zmíněné adrese byste měli v prohlížeči vidět použitý text.</p> +<p>Proměnná prostředí <code>FLASK_APP</code> říká Flasku, kde aplikaci najít. +V daném souboru Flask hledá automaticky proměnnou jménem <code>app</code>. +(<a href="https://flask.palletsprojects.com/en/1.1.x/cli/">Jde nastavit</a> i jiná.) +Proměnná <code>FLASK_DEBUG</code> nastavuje ladícím režim, který si popíšeme za chvíli.</p> +<p>V programu jsme jako <code>app</code> vytvořili flaskovou aplikaci. +Argument <code>__name__</code> je jméno modulu – Flask podle něj hledá soubory, +které k aplikaci patří (viz <code>static</code> a <code>templates</code> níže).</p> +<p>Pomocí dekorátoru <a href="https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.route"><code>@app.route</code></a> jsme zaregistrovali takzvaný <em>view</em> (pohled) – +funkci, která vrací obsah pro danou <a href="naucse:page?lesson=fast-track/http#url-anatomy">cestu v URL</a>. +Tomuto spojení cesty a pohledové funkce se říká <em>route</em> (nebo počeštěně „routa“). +My konkrétně říkáme, že na cestě <code>/</code> (tedy na „domovské stránce“) bude +k dispozici obsah, který vrátí funkce <code>index</code>.</p> +<p>Více různých adres lze obsloužit jednoduše přidáním dalších funkcí:</p> +<div class="highlight"><pre><span></span><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">index</span><span class="p">():</span> + <span class="k">return</span> <span class="s1">'Index Page'</span> + +<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/hello/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">hello</span><span class="p">():</span> + <span class="k">return</span> <span class="s1">'Hello, World'</span> +</pre></div><p>Na adrese <a href="http://127.0.0.1:5000/hello/"><code>http://127.0.0.1:5000/hello/</code></a> pak uvidíte druhou stránku.</p> +<h3 id="ladici_rezim">Ladící režim +<a href="#ladici_rezim" class="header-link">#</a> +</h3> +<p>Proměnná <code>FLASK_DEBUG</code> říká, že se aplikace má spustit v ladícím režimu: +je zapnutý příjemnější výpis chyb a aplikace se automaticky restartuje +po změnách.</p> +<p>Zkuste ve funkci <code>hello()</code> vyvolat výjimku (například dělení nulou – <code>1/0</code>) +a podívat se, jak chyba v ladícím režimu „vypadá“: +Flask ukáže <em>traceback</em> podobný tomu z příkazové řádky a navíc vám na každé +úrovni umožní pomocí malé ikonky spustit konzoli. +Bezpečnostní PIN k této konzoli najdete v terminálu, kde server běží.</p> +<p>Ladící režim je užitečný, ale nebezpečný – návštěvníkům stránky může +(po prolomení celkem jednoduchého „hesla“) umožnit spustit jakýkoli pythonní kód. +Navíc aplikaci zpomaluje. +Používejte ho proto <em>pouze</em> na svém počítači.</p> +<h3 id="dynamicke_routy">Dynamické routy +<a href="#dynamicke_routy" class="header-link">#</a> +</h3> +<p>Když vytváříte dynamický web, ne vždy můžete všechna URL znát dopředu. +Budete například chtít zobrazit informace o uživatelích na adresách +jako <code>/user/hroncok/</code>, ale nemůžete při každé registraci nového uživatele +přidávat novou funkci do kódu. +Musíte použít <a href="https://flask.palletsprojects.com/en/1.1.x/quickstart/#variable-rules">dynamické routy</a>:</p> +<div class="highlight"><pre><span></span><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/user/<username>/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">profile</span><span class="p">(</span><span class="n">username</span><span class="p">):</span> + <span class="k">return</span> <span class="s1">'User </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">username</span><span class="p">)</span> +</pre></div><p>Proměnnou část cesty ohraničíte lomenými závorkami a použijte jako parametr +funkce. Pokud chcete, můžete specifikovat, na jaký obsah se pravidlo vztahuje. +Například číselný identifikátor článku pro adresy jako <code>/post/42/</code> můžete zadat +takto:</p> +<div class="highlight"><pre><span></span><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/post/<int:post_id>/'</span><span class="p">)</span> +</pre></div><p>Můžete použít různá pravidla, např.:</p> +<ul> +<li><code>string</code> akceptuje jakýkoliv text bez lomítek (výchozí)</li> +<li><code>int</code> akceptuje celá čísla (a pohledové funkci je předá jako <code>int</code>, ne text)</li> +<li><code>float</code> akceptuje i desetinná čísla s tečkou (a předá je jako <code>float</code>)</li> +<li><code>path</code> akceptuje text i s lomítky</li> +</ul> +<p>Rout můžete definovat i víc pro jednu funkci. +Často se to používá s výchozí hodnotou argumentu:</p> +<div class="highlight"><pre><span></span><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/hello/'</span><span class="p">)</span> +<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/hello/<name>/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'world'</span><span class="p">):</span> + <span class="k">return</span> <span class="s1">'Hello, </span><span class="si">{}</span><span class="s1">!'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> +</pre></div><h3 id="ziskani_url">Získání URL +<a href="#ziskani_url" class="header-link">#</a> +</h3> +<p>Opačným způsobem jak k routám přistupovat je, když potřebujete získat URL +nějaké stránky, například protože potřebujete zobrazit odkaz. +K tomu se používá funkce <a href="https://flask.palletsprojects.com/en/1.1.x/api/#flask.url_for"><code>url_for()</code></a>, která jako první parametr bere jméno +routy (neboli jméno funkce, která routu obsluhuje), a pak pojmenované argumenty +pro pravidla v dynamické routě:</p> +<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">url_for</span> + +<span class="o">...</span> + +<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/url/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">show_url</span><span class="p">():</span> + <span class="k">return</span> <span class="n">url_for</span><span class="p">(</span><span class="s1">'profile'</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s1">'hroncok'</span><span class="p">)</span> +</pre></div><p>Tuto funkci jde použít jen uvnitř pohledové funkce, +Pokud ji chcete vyzkoušet například v interaktivní konzoli, +můžete použít speciální kontext:</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">():</span> +<span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s1">'profile'</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s1">'hroncok'</span><span class="p">))</span> +<span class="gp">... </span> +<span class="go">/user/hroncok/</span> +</pre></div><p>Možná si říkáte, proč tu URL prostě nevytvořit ručně. +S takovým přístupem byste ale mohli narazit na problém, pokud cestu později +změníte – což se může stát např. i když web nasadíte na jiný server. +Generování URL vám také může zjednodušit nasazení statické verze stránek.</p> +<p>Pro URL v rámci vašich stránek proto doporučujeme <code>url_for</code> používat důsledně.</p> +<h3 id="sablony">Šablony +<a href="#sablony" class="header-link">#</a> +</h3> +<p>Zatím jsou naše webové stránky poměrně nudné: obsahují jen prostý text, +nepoužívají HTML.</p> +<div class="admonition note"><p>Předpokládáme, že víte co je to <a href="https://developer.mozilla.org/en-US/docs/Web/HTML">HTML</a> a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS">CSS</a>. +Jestli ne, doporučujeme si projít základy těchto webových technologií +např. na stránkách <a href="https://developer.mozilla.org/en-US/docs/Web">MDN</a>.</p> +</div><p>Klidně byste mohli udělat něco jako:</p> +<div class="highlight"><pre><span></span><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">hello</span><span class="p">():</span> + <span class="k">return</span> <span class="s1">'<html><head><title>...'</span> +</pre></div><p>...ale asi by to nebylo příliš příjemné. +Python je jazyk dělaný na popis algoritmů, procesů a logiky spíš než obsahu. +Lepší je HTML dát do zvláštního souboru a použít ho jako <em>šablonu</em> +(angl. <em>template</em>). +Z Flasku vypadá použití šablony takto:</p> +<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">render_template</span> + +<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/hello/'</span><span class="p">)</span> +<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/hello/<name>/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> + <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s1">'hello.html'</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">)</span> +</pre></div><p>Funkce <code>render_template</code> nejen vrátí HTML z daného souboru, ale umí do něj +i doplnit informace, které dostane v pojmenovaných argumentech.</p> +<p>Ukažme si to na příkladu: vedle souboru s kódem vytvořte složku <code>templates</code> +a v ní <code>hello.html</code> s tímto obsahem:</p> +<div class="highlight"><pre><span></span><span class="cp"><!doctype html></span> +<span class="p"><</span><span class="nt">html</span><span class="p">></span> + <span class="p"><</span><span class="nt">head</span><span class="p">></span> + <span class="p"><</span><span class="nt">title</span><span class="p">></span>Hello from Flask<span class="p"></</span><span class="nt">title</span><span class="p">></span> + <span class="p"></</span><span class="nt">head</span><span class="p">></span> + <span class="p"><</span><span class="nt">body</span><span class="p">></span> + <span class="cp">{%</span> <span class="k">if</span> <span class="nv">name</span> <span class="cp">%}</span> + <span class="p"><</span><span class="nt">h1</span><span class="p">></span>Hello <span class="cp">{{</span> <span class="nv">name</span> <span class="cp">}}</span>!<span class="p"></</span><span class="nt">h1</span><span class="p">></span> + <span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">'hello'</span><span class="o">)</span> <span class="cp">}}</span><span class="s">"</span><span class="p">></span>Go back home<span class="p"></</span><span class="nt">a</span><span class="p">></span> + <span class="cp">{%</span> <span class="k">else</span> <span class="cp">%}</span> + <span class="p"><</span><span class="nt">h1</span><span class="p">></span>Hello, World!<span class="p"></</span><span class="nt">h1</span><span class="p">></span> + <span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span> + <span class="p"></</span><span class="nt">body</span><span class="p">></span> +<span class="p"></</span><span class="nt">html</span><span class="p">></span> +</pre></div><p>Šablony používají (jako výchozí variantu) šablonovací jazyk <a href="https://jinja.palletsprojects.com/en/2.10.x/templates/">Jinja2</a>, +který se s Flaskem a jinými frameworky pro Python používá často. +Kompletní popis jazyka najdete v <a href="https://jinja.palletsprojects.com/en/2.10.x/templates/">dokumentaci</a>, ale +pro většinu stránek se obejdete s doplněním hodnoty (<code>{{ promenna }}</code>) +a podmíněným obsahem (<code>{% if %}</code>) jako výše, +případně s <a href="https://jinja.palletsprojects.com/en/2.10.x/templates/#for">cyklem</a>: <code>{% for %}</code>/<code>{% endfor %}</code>. Ve větších +aplikacích se pak hodí použití <code>{% include ... %}</code>, <code>{% extends ... %}</code> +a případně také tvorba maker <code>{% macro ... %}</code>/<code>{% endmacro %}</code>.</p> +<p>Veškerý kontext (proměnné) do šablony musí přijít z volání <code>render_template()</code>. +Navíc můžete použít vybrané funkce, např. <code>url_for()</code>. +(Jiné funkce známé z Pythonu ale použít nejdou – ač jsou podobné, je Jinja2 +jiný jazyk než Python.)</p> +<h4 id="filtry">Filtry +<a href="#filtry" class="header-link">#</a> +</h4> +<p>Není úplně elegantní vzít nějaká data (např. tweety z Twitter API) a ještě před +předáním šabloně do nich cpát svoje úpravy (např. převod na HTML). +Od toho jsou tu filtry. Filtr transformuje hodnotu na řetězec, +který pak ukážeme uživateli.</p> +<p>Zde je například filtr <code>time</code>, který načte čas v určitém formátu +a převede ho do jiného:</p> +<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span> + +<span class="nd">@app</span><span class="o">.</span><span class="n">template_filter</span><span class="p">(</span><span class="s1">'time'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">convert_time</span><span class="p">(</span><span class="n">text</span><span class="p">):</span> +<span class="w"> </span><span class="sd">"""Convert the time format to a different one"""</span> + <span class="n">dt</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">strptime</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="s1">'</span><span class="si">%a</span><span class="s1"> %b </span><span class="si">%d</span><span class="s1"> %H:%M:%S %z %Y'</span><span class="p">)</span> + <span class="k">return</span> <span class="n">dt</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'</span><span class="si">%c</span><span class="s1">'</span><span class="p">)</span> + +<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/date_example'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">date_example</span><span class="p">():</span> + <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span> + <span class="s1">'date_example.html'</span><span class="p">,</span> + <span class="n">created_at</span><span class="o">=</span><span class="s1">'Tue Mar 21 15:50:59 +0000 2017'</span><span class="p">,</span> + <span class="p">)</span> +</pre></div><p>V šabloně <code>date_example.html</code> se pak filtr použije pomocí svislítka:</p> +<div class="highlight"><pre><span></span><span class="cp">{{</span> <span class="nv">created_at</span><span class="o">|</span><span class="nf">time</span> <span class="cp">}}</span> +</pre></div><p>Pokud potřebujete velmi obecný filtr, je vhodné se podívat do <a href="https://jinja.palletsprojects.com/en/2.10.x/templates/#builtin-filters">seznamu těch vestavěných</a>.</p> +<h4 id="escaping">Escaping +<a href="#escaping" class="header-link">#</a> +</h4> +<p>V textu, který se vkládá do šablon, jsou automaticky nahrazeny znaky, které +mají v HTML speciální význam. +Zabraňuje se tak bezpečnostním rizikům, kdy se vstup od uživatele interpretuje +jako HTML.</p> +<p>Například když v aplikaci výše navštívíme URL <code>/hello/<script>alert("Foo")/</code>, +bude výsledné HTML vypadat takto:</p> +<div class="highlight"><pre><span></span><span class="cp"><!doctype html></span> +<span class="p"><</span><span class="nt">title</span><span class="p">></span>Hello from Flask<span class="p"></</span><span class="nt">title</span><span class="p">></span> + + <span class="p"><</span><span class="nt">h1</span><span class="p">></span>Hello <span class="ni">&lt;</span>script<span class="ni">&gt;</span>alert(<span class="ni">&#34;</span>Foo<span class="ni">&#34;</span>)!<span class="p"></</span><span class="nt">h1</span><span class="p">></span> +</pre></div><div class="admonition note"><p>Některé prohlížeče (či doplňky do nich) proti podobným útokům různým způsobem +chrání. Budete-li na své stránky zkoušet „zaútočit”, zkontrolujte v konzoli +URL, které vaše aplikace v požadavku reálně dostává. +Pro příklad výše to může být <code>/hello/%3Cscript%3Ealert(%22Foo%22)/</code>.</p> +</div><p>Někdy je ovšem potřeba do stránky opravdu vložit HTML. +To se dá zajistit dvěma způsoby. Nejjednodušší je vestavěný filtr <code>safe</code>:</p> +<div class="highlight"><pre><span></span><span class="cp">{{</span> <span class="s2">"<em>Text</em>"</span> <span class="o">|</span> <span class="nf">safe</span> <span class="cp">}}</span> +</pre></div><p>V Pythonu pak lze použít <a href="https://jinja.palletsprojects.com/en/2.10.x/api/#jinja2.Markup">jinja2.Markup</a>, +čímž se daný text označí jako „bezpečný”.</p> +<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">jinja2</span> + +<span class="nd">@app</span><span class="o">.</span><span class="n">template_filter</span><span class="p">(</span><span class="s1">'time'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">convert_time</span><span class="p">(</span><span class="n">text</span><span class="p">):</span> +<span class="w"> </span><span class="sd">"""Convert the time format to a different one"""</span> + <span class="n">dt</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">strptime</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="s1">'</span><span class="si">%a</span><span class="s1"> %b </span><span class="si">%d</span><span class="s1"> %H:%M:%S %z %Y'</span><span class="p">)</span> + <span class="n">result</span> <span class="o">=</span> <span class="n">dt</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'<strong></span><span class="si">%c</span><span class="s1"></strong>'</span><span class="p">)</span> + <span class="k">return</span> <span class="n">jinja2</span><span class="o">.</span><span class="n">Markup</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> +</pre></div><p>Při použití <code>safe</code> a <code>Markup</code> však vždycky myslete na to, aby nikdo +(ani nikdo mnohem chytřejší než vy) nemohl na vaší stránce provést něco +nekalého.</p> +<h3 id="staticke_soubory">Statické soubory +<a href="#staticke_soubory" class="header-link">#</a> +</h3> +<p>Pokud budete potřebovat nějaké statické soubory (např. styly CSS nebo +obrázky), dejte je do adresáře <code>static</code> vedle souboru s kódem +a přistupujte k nim pomocí routy <code>static</code>:</p> +<div class="highlight"><pre><span></span><span class="n">url_for</span><span class="p">(</span><span class="s1">'static'</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="s1">'style.css'</span><span class="p">)</span> +</pre></div><p>V šabloně pak například:</p> +<div class="highlight"><pre><span></span><span class="p"><</span><span class="nt">link</span> <span class="na">href</span><span class="o">=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">'static'</span><span class="o">,</span> <span class="nv">filename</span><span class="o">=</span><span class="s1">'style.css'</span><span class="o">)</span> <span class="cp">}}</span><span class="s">"</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span><span class="p">></span> +</pre></div><h3 id="logovani">Logování +<a href="#logovani" class="header-link">#</a> +</h3> +<p>Při vytváření webové aplikace chcete často komunikovat nejen prostřednictvím +HTTP odpovědí na dotazy (ať už ve formě webové stránky, JSON odpovědi či jiného +formatu), ale také vypisovat různé chybové, informační či ladící hlášky na +straně serveru. Možností je použít například funkci <code>print</code>, ale ta není +dostatečně flexibilní. Brzy narazíte na problémy, jako že výstup není konzistentní +s jinými hláškami z Flasku, že pro různé typy výpisů, časová razítka, přesměrování +logu do souboru a další potřebujete vytvářet spoustu logiky kolem namísto vytváření +samotné webové aplikace.</p> +<p>Řešením je použít standardní logovací modul <a href="https://docs.python.org/3/library/logging.html#module-logging">logging</a>, který řeší vše potřebné +(úrovně zpráv, filtry, formátování časového razítka a dalších meta-informací o +běhu programu) a výstup bude konzistentní s jinými aplikacemi (jiní správci +vaší webové aplikace pak nebudou z formátu výstupů zmatení). Protože používáme +Flask a ten také <a href="https://flask.palletsprojects.com/en/1.1.x/logging/">loguje tímto modulem</a>, +stačí použít předpřipravený <code>app.logger</code>.</p> +<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> + +<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span> +<span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">"I've just initialized the Flask app"</span><span class="p">)</span> + +<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">index</span><span class="p">():</span> + <span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s1">'Someone is accessing the index page!'</span><span class="p">)</span> + <span class="k">return</span> <span class="s1">'Index Page'</span> +</pre></div><p>Ve výchozím nastavení se loguje pouze od úrovně upozornění výše (<code>warning</code>, +<code>error</code>, <code>critical</code>). Při spuštění aplikace v ladícím režimu se loguje vše +(navíc i <code>debug</code> a <code>info</code>). Aktuální úroveň je možné také změnit pomocí metody +<code>setLevel</code>, viz dokumentace modulu <a href="https://docs.python.org/3/library/logging.html#module-logging">logging</a>.</p> +<h3 id="vetsi_flask_aplikace">Větší Flask aplikace +<a href="#vetsi_flask_aplikace" class="header-link">#</a> +</h3> +<p>Flask je sice označován jako mikroframework, to ale neznamená, že jej nelze +použít na větší a složitější webové aplikace. Pokud chcete vytvářet vytvářet +aplikaci s databází a ORM modely, je nutné propojit Flask s dalšími knihovnami +(například <a href="https://flask-sqlalchemy.palletsprojects.com/en/2.x/">flask-sqlalchemy</a>, +nebo <a href="https://flask-pymongo.readthedocs.io/en/latest/">flask-pymongo</a>).</p> +<p>Jiné frameworky postavené nad <a href="https://cs.wikipedia.org/wiki/Model-view-controller">Model-View-Controller</a> +paradigmatem mají tyto vlastnosti již zabudovány v sobě (například <a href="https://www.djangoproject.com/">Django</a>).</p> +<p>Následující sekce popisují některé zajímavé techniky, které se mohou u větších +a složitějších aplikací hodit.</p> +<h4 id="create_app_factory">create_app factory +<a href="#create_app_factory" class="header-link">#</a> +</h4> +<p>Mimo vytváření Flask aplikace přímo v modulu, je možné aplikaci tvořit pomocí +funkce, tzv. <a href="https://flask.palletsprojects.com/en/1.1.x/patterns/appfactories/"><code>application_factory</code></a>, +standardně pojmenované <code>create_app</code>. Takový přístup má výhodu, že aplikace se +neinicializuje hned při importu modulu, ale až při zavolání funkce. Voláním funkce +můžete navíc předat i konfigurační parametry (typicky cesta ke konfiguračnímu +souboru). Díky tomu lze snadněji vytvářet Flask aplikace s různými konfiguracemi +pro testování nebo dokonce vytvářet více Flask aplikací v rámci jednoho Python skriptu.</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">create_app</span><span class="p">(</span><span class="n">config</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> + <span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span> + + <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_pyfile</span><span class="p">(</span><span class="n">config</span> <span class="ow">or</span> <span class="s1">'config.py'</span><span class="p">)</span> + <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'the_answer'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">42</span> + <span class="n">app</span><span class="o">.</span><span class="n">secret_key</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'MY_SECRET'</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span> + + <span class="k">return</span> <span class="n">app</span> +</pre></div><h4 id="blueprint_moduly">Blueprint moduly +<a href="#blueprint_moduly" class="header-link">#</a> +</h4> +<p>Ve velkých webových aplikacích je již vhodné seskupovat jednotlivé pohledy do +samostatných celků. K tomuto účelu slouží ve Flasku <a href="https://flask.palletsprojects.com/en/1.1.x/blueprints/">blueprinty</a> (hezky česky +„modrotisk” nebo také <a href="https://cs.wikipedia.org/wiki/Diazotypie">„modrák”</a>). +Výhodou je, že můžete vytvořit blueprint (instanci +třídy <a href="https://flask.palletsprojects.com/en/1.1.x/api/#flask.Blueprint">Blueprint</a>) s několika views, vlastní <code>templates</code> složkou a dalším +nastavením nezávisle na tom, v jaké Flask aplikaci pak bude použitý. Takový +blueprint pak můžete využívat i v několika různých aplikacích a snadno tak +dosáhnout znovupouželnosti.</p> +<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Blueprint</span> + +<span class="n">auth</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s1">'auth'</span><span class="p">,</span> <span class="vm">__name__</span><span class="p">,</span> <span class="n">template_folder</span><span class="o">=</span><span class="s1">'templates'</span><span class="p">)</span> + +<span class="nd">@auth</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/login'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">login</span><span class="p">():</span> + <span class="o">...</span> + +<span class="nd">@auth</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/logout'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">logout</span><span class="p">():</span> + <span class="o">...</span> + +<span class="nd">@auth</span><span class="o">.</span><span class="n">app_template_filter</span><span class="p">(</span><span class="s1">'userlink'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">user_link</span><span class="p">(</span><span class="n">username</span><span class="p">):</span> + <span class="o">...</span> +</pre></div><p>Blueprint pak stačí ve Flask aplikaci <a href="https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.register_blueprint">registrovat</a> +a je jedno, zda ji vytváříte pomocí <code>create_app</code> nebo napřímo. Navíc můžete mimo +jiné přidat i prefix pro všechny cesty v blueprintu.</p> +<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> +<span class="kn">from</span> <span class="nn">auth.views</span> <span class="kn">import</span> <span class="n">auth</span> + +<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span> +<span class="c1"># this will create the /auth/login and /auth/logout endpoints</span> +<span class="n">app</span><span class="o">.</span><span class="n">register_blueprint</span><span class="p">(</span><span class="n">auth</span><span class="p">,</span> <span class="n">url_prefix</span><span class="o">=</span><span class="s1">'/auth'</span><span class="p">)</span> +</pre></div><p>V případě použití <code>url_for</code> je třeba cesty z blueprintu namespacovat, např.:</p> +<div class="highlight"><pre><span></span><span class="n">url_for</span><span class="p">(</span><span class="s1">'auth.login'</span><span class="p">)</span> +</pre></div><h4 id="vlastni_podtrida_flask">Vlastní podtřída Flask +<a href="#vlastni_podtrida_flask" class="header-link">#</a> +</h4> +<p>Třída <code>Flask</code> je uzpůsobena k tomu, aby bylo možné snadno rozšiřovat a přepisovat +výchozí chování. Mimo přidávání vlastních metod lze například měnit třídy, které +budou použity pro HTTP požadavky a odpovědi, měnit výchozí konfiguraci <code>flask</code> a +spoustu dalšího. Nezapomeňte volat konstruktor nadtřídy.</p> +<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">current_app</span><span class="p">,</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">Response</span> + +<span class="k">class</span> <span class="nc">MIPYTResponse</span><span class="p">(</span><span class="n">Response</span><span class="p">):</span> + <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> + <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> + <span class="bp">self</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="s1">'MI-PYT'</span><span class="p">,</span> <span class="s1">'best'</span><span class="p">)</span> + + +<span class="k">class</span> <span class="nc">GreeterApp</span><span class="p">(</span><span class="n">Flask</span><span class="p">):</span> + <span class="n">response_class</span> <span class="o">=</span> <span class="n">MIPYTResponse</span> + + <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> + <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> + <span class="bp">self</span><span class="o">.</span><span class="n">greetings</span> <span class="o">=</span> <span class="mi">0</span> + + <span class="k">def</span> <span class="nf">greet</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">greetings</span> <span class="o">+=</span> <span class="mi">1</span> + <span class="k">return</span> <span class="s1">'Hello!'</span> + + +<span class="n">app</span> <span class="o">=</span> <span class="n">GreeterApp</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span> + + +<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">greet</span><span class="p">():</span> + <span class="k">return</span> <span class="n">current_app</span><span class="o">.</span><span class="n">greet</span><span class="p">()</span> + + +<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/number/'</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">greetings_number</span><span class="p">():</span> + <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">current_app</span><span class="o">.</span><span class="n">greetings</span><span class="p">)</span> +</pre></div><h3 id="a_dalsi">A další +<a href="#a_dalsi" class="header-link">#</a> +</h3> +<p>Flask umí i další věci – například zpracování formulářů, chybové stránky nebo +přesměrování. Také existuje i řada <a href="https://flask.palletsprojects.com/en/1.1.x/extensions/?highlight=extensions">rozšíření</a>, +které mohou ušetřit práci s běžnými úkony jako například správa uživatelů, +tvorba REST API nebo integrace s různými službami.</p> +<p>Všechno to najdete +<a href="https://flask.palletsprojects.com/en/1.1.x/quickstart/">v dokumentaci</a>.</p> \ No newline at end of file diff --git a/lessons/fstring/index.html b/lessons/fstring/index.html new file mode 100644 index 00000000..7a166210 --- /dev/null +++ b/lessons/fstring/index.html @@ -0,0 +1,75 @@ +<h1 id="sablony_formatovaci_retezce">Šablony (formátovací řetězce) +<a href="#sablony_formatovaci_retezce" class="header-link">#</a> +</h1> +<p>Řekněme, že chceš uživateli vypsat určitou hodnotu s nějakou „omáčkou“ okolo. +Dá se na to použít <code>print()</code>, kterému můžeš předat „mix“ řetězců a čísel:</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">soucet</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">4</span> +<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="s1">'Součet je'</span><span class="p">,</span> <span class="n">soucet</span><span class="p">)</span> +</pre></div><p>Co ale když chceš celý tento výpis uložit do proměnné – jako jeden řetězec? +Čárka tu fungovat nebude, ta odděluje argumenty ve volání funkce. +Je potřeba <code>soucet</code> převést na řetězec a ten pak připojit k „omáčce“:</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">hlaska</span> <span class="o">=</span> <span class="s1">'Součet je '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">soucet</span><span class="p">)</span> +</pre></div><p>To ale není tak přehledné, jak by mohlo. +Lze to zpřehlednit použitím šablony.</p> +<p>Takovou šablonu si představ jako formulář s vynechanými místy:</p> +<div class="highlight"><pre><code>Součet je __________. +</code></pre></div><p>Složitější šablona by byla třeba tahle:</p> +<div class="highlight"><pre><code>Mil[ý/á] _______, +Váš výsledek je __________. + +S pozdravem, +_________ +</code></pre></div><p>Aby Python věděl, do kterého vynechaného místa co doplnit, je potřeba +jednotlivá vynechaná místa ve formuláři nějak jednoznačně označit. +Použijme jména v „kudrnatých“ závorkách:</p> +<div class="highlight"><pre><code>Součet je {soucet}. +</code></pre></div><div class="highlight"><pre><code>Mil{y_a} {osloveni}, +Váš výsledek je {soucet}. + +S pozdravem, +{podpis}. +</code></pre></div><p>Takovou šablonu můžeš použít jako <em>formátovací řetězec</em> +(angl. <a href="https://docs.python.org/3.6/reference/lexical_analysis.html#formatted-string-literals"><em>formatted string literal</em></a>, +zkráceně <em>f-string</em>). +Jako jakýkoli jiný řetězec ji vlož do uvozovek. +A aby bylo jasné, že jde o šablonu, před první uvozovky přidej navíc značku <code>f</code>.</p> +<div class="highlight"><pre><span></span><span class="sa">f</span><span class="s2">"Součet je </span><span class="si">{</span><span class="n">soucet</span><span class="si">}</span><span class="s2">."</span> +</pre></div><p>Takový formátovací řetězec jde použít v Pythonu – jako jakýkoli jiný řetězec:</p> +<div class="highlight"><pre><span></span><span class="n">soucet</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">4</span> +<span class="n">hlaska</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">'Součet je </span><span class="si">{</span><span class="n">soucet</span><span class="si">}</span><span class="s1">'</span> +<span class="nb">print</span><span class="p">(</span><span class="n">hlaska</span><span class="p">)</span> +</pre></div><div class="highlight"><pre><span></span><span class="n">y_a</span> <span class="o">=</span> <span class="s1">'á'</span> +<span class="n">osloveni</span> <span class="o">=</span> <span class="s1">'Anežko'</span> +<span class="n">soucet</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">4</span> +<span class="n">podpis</span> <span class="o">=</span> <span class="s1">'Váš Program'</span> + +<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"""</span> +<span class="s2">Mil</span><span class="si">{</span><span class="n">y_a</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">osloveni</span><span class="si">}</span><span class="s2">,</span> +<span class="s2">Váš výsledek je </span><span class="si">{</span><span class="n">soucet</span><span class="si">}</span><span class="s2">.</span> + +<span class="s2">S pozdravem,</span> +<span class="si">{</span><span class="n">podpis</span><span class="si">}</span> +<span class="s2">"""</span><span class="p">)</span> +</pre></div><p>A nakonec – v šabloně můžeš použít nejen jména proměnných, ale jakékoli výrazy.</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">hlaska</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">'Součet je </span><span class="si">{</span><span class="mi">3</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">4</span><span class="si">}</span><span class="s1">'</span> +</pre></div><p>Ale nepřežeň to! +Většinou je program přehlednější, když si každou vypisovanou hodnotu zvlášť +pojmenuješ – tedy uložíš do vhodně pojmenované proměnné.</p> +<h2 id="metoda_format">Metoda format +<a href="#metoda_format" class="header-link">#</a> +</h2> +<p>Někdy se stane, že jednu šablonu potřebuješ použít vícekrát. +Pak formátovací řetězec použít nemůžeš, protože se do něj proměnné doplňují +automaticky a hned. +V takovém případě můžeš šablonu napsat do normálního řetězce (bez <code>f</code> na +začátku) a použít metodu <code>format</code>:</p> +<div class="highlight"><pre><span></span><span class="n">sablona</span> <span class="o">=</span> <span class="s1">'Ahoj </span><span class="si">{jmeno}</span><span class="s1">! Tvoje číslo je </span><span class="si">{cislo}</span><span class="s1">.'</span> +<span class="nb">print</span><span class="p">(</span><span class="n">sablona</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">cislo</span><span class="o">=</span><span class="mi">7</span><span class="p">,</span> <span class="n">jmeno</span><span class="o">=</span><span class="s1">'Hynku'</span><span class="p">))</span> +<span class="nb">print</span><span class="p">(</span><span class="n">sablona</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">cislo</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span> <span class="n">jmeno</span><span class="o">=</span><span class="s1">'Viléme'</span><span class="p">))</span> +<span class="nb">print</span><span class="p">(</span><span class="n">sablona</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">cislo</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">jmeno</span><span class="o">=</span><span class="s1">'Jarmilo'</span><span class="p">))</span> +</pre></div><p>Oproti formátovacím řetězcům umí <code>format</code> užitečnou zkratku: nepojmenované +argumenty dosadí postupně do nepojmenovaných míst v šabloně:</p> +<div class="highlight"><pre><span></span><span class="n">vypis</span> <span class="o">=</span> <span class="s1">'</span><span class="si">{}</span><span class="s1"> krát </span><span class="si">{}</span><span class="s1"> je </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">3</span> <span class="o">*</span> <span class="mi">4</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">vypis</span><span class="p">)</span> +</pre></div><p>Výrazy jako <code>f'Součet je {3 + 4}'</code> ale <code>format</code> dosadit neumí. +Složitější dosazované hodnoty si proto vždycky pojmenuj.</p> \ No newline at end of file diff --git a/lessons/beginners/functions/static/call-anatomy.svg b/lessons/functions/call-anatomy.svg similarity index 100% rename from lessons/beginners/functions/static/call-anatomy.svg rename to lessons/functions/call-anatomy.svg diff --git a/lessons/functions/index.html b/lessons/functions/index.html new file mode 100644 index 00000000..abc9c91e --- /dev/null +++ b/lessons/functions/index.html @@ -0,0 +1,236 @@ +<h1 id="funkce">Funkce +<a href="#funkce" class="header-link">#</a> +</h1> +<p>Známe spoustu matematických operací, které se zapisují pomocí symbolů – třeba +plus a minus. +Python se snaží používat stejné symboly jako matematici:</p> +<ul> +<li>3 + 4</li> +<li><var>a</var> - <var>b</var></li> +</ul> +<p>S násobením a dělením už je to složitější. +Matematický zápis se na běžné klávesnici nedá napsat:</p> +<ul> +<li>3 · 4</li> +<li>¾</li> +</ul> +<p>V Pythonu si ale pořád vystačíme se symbolem, byť trochu jiným – <code>*</code>, <code>/</code>.</p> +<p>Matematici ale píšou na papír, a tak si můžou dovolit vymýšlet stále +zajímavější klikyháky, které se pak na klávesnici píšou stále hůř:</p> +<ul> +<li><var>x</var>²</li> +<li><var>x</var> ≤ <var>y</var></li> +<li>sin θ</li> +<li>Γ(<var>x</var>)</li> +<li>∫<var>x</var></li> +<li>|<var>s</var>|</li> +<li>⌊<var>x</var>⌋</li> +<li><var>a</var> ★ <var>b</var></li> +<li><var>a</var> ⨁ <var>b</var></li> +</ul> +<p>Ne že by neexistovaly programovací jazyky, +na které je potřeba speciální klávesnice. +Třeba program v jazyce APL laik jednoduše ani nenapíše, ani nepřečte:</p> +<!--z http://catpad.net/michael/apl/ --> + +<div class="highlight"><pre><code>⍎’⎕’,∈Nρ⊂S←’←⎕←(3=T)∨M∧2=T←⊃+/(V⌽”⊂M),(V⊖”⊂M),(V,⌽V)⌽”(V,V←1¯1)⊖”⊂M’ +</code></pre></div><p>Expert v APL může být vysoce produktivní, ale Python se zaměřuje spíš na to, +aby se dal snadno naučit. +A tak používá symboly jen pro ty nejčastější operace. +Operátorů, které využívají symboly, je tak málo, že už jich zhruba půlku znáš!</p> +<div class="admonition note"><p>Pro zajímavost, tady jsou všechny – i ty co ještě neznáš:</p> +<!-- +“Operátor” není přesně definovaný termín. +Tenhle seznam by měl odpovídat https://docs.python.org/3/reference/expressions.html#operator-precedence +(jsou to operátory jako kousky výrazů, ne lexikální jednotky (https://docs.python.org/3/reference/lexical_analysis.html#operators) +ani “základní” operace (https://docs.python.org/3/library/operator.html). +Proto tu není =, +=, *= atd., které se ve výrazu použít nedají. +--> + +<div> + <code>==</code> <code>!=</code> + <code><</code> <code>></code> + <code><=</code> <code>>=</code> + <code class="text-muted">:=</code> + <code class="text-muted">|</code> <code class="text-muted">^</code> + <code class="text-muted">&</code> + <code class="text-muted"><<</code> <code class="text-muted">>></code> + <code>+</code> <code>-</code> + <code>*</code> <code class="text-muted">@</code> <code>/</code> + <code>//</code> <code>%</code> + <code class="text-muted">~</code> + <code>**</code> + <code class="text-muted">[ ]</code> <code class="text-muted">( )</code> + <code class="text-muted">{ }</code> + <code class="text-muted">.</code> +</div> + +</div><p>Všechno ostatní vyjádříme slovně.</p> +<h2 id="delka_retezce">Délka řetězce +<a href="#delka_retezce" class="header-link">#</a> +</h2> +<p>Jedna operace, na kterou v Pythonu není symbol, je zjištění délky řetězce. +Místo symbolu má název. +Jmenuje se <code>len</code> (z angl. <em>length</em>, délka), a používá se takto:</p> +<div class="highlight"><pre><span></span><span class="n">slovo</span> <span class="o">=</span> <span class="s1">'Ahoj'</span> +<span class="n">delka</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">slovo</span><span class="p">)</span> <span class="c1"># Vypočítání délky</span> +<span class="nb">print</span><span class="p">(</span><span class="n">delka</span><span class="p">)</span> +</pre></div><p>To <code>len</code> je <em>funkce</em> (angl. <em>function</em>). +Jak se takové funkce používají?</p> +<p>K tomu, abys funkci mohl/a použít, potřebuješ znát její +<em>jméno</em> – tady <code>len</code>. +Za jméno funkce patří závorky, +do nichž uzavřeš <em>argument</em> (neboli <em>vstup</em>) funkce. +To je informace, se kterou bude funkce +pracovat – třeba <code>len</code> ze svého argumentu vypočítá délku.</p> +<p>Celému výrazu <code>len(slovo)</code> se říká <em>volání funkce</em> (angl. <em>function call</em>). +Jeho výsledek, takzvaná <em>návratová</em> hodnota +(angl. <em>return value</em>) se dá třeba přiřadit do proměnné.</p> +<p><span class="figure"><a href="naucse:static?filename=call-anatomy.svg"><img src="naucse:static?filename=call-anatomy.svg" alt="Diagram volání funkce"></a></span></p> +<div class="admonition note"><p class="admonition-title">Pro matematiky</p> +<p>Máš-li rád/a matematiku, dej pozor! +Funkce v Pythonu je něco jiného než funkce v matematice, +i když se stejně jmenují a podobně zapisují. +Pythonní funkce může např. mít pro stejný argument různé hodnoty.</p> +</div><h3 id="volani_funkce_jako_vyraz">Volání funkce jako výraz +<a href="#volani_funkce_jako_vyraz" class="header-link">#</a> +</h3> +<p>Vzpomínáš si, jak Python vyhodnocuje výrazy?</p> +<div class="highlight"><pre><span></span><span class="n">vysledek</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">*</span> <span class="p">(</span><span class="mi">5</span> <span class="o">+</span> <span class="mi">2</span><span class="p">)</span> +<span class="c1"># ╰──┬──╯</span> +<span class="n">vysledek</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">*</span> <span class="mi">7</span> +<span class="c1"># ╰─┬────╯</span> +<span class="n">vysledek</span> <span class="o">=</span> <span class="mi">21</span> +</pre></div><p>Volání funkce je taky výraz. +Stejně jako <code>a + b</code> je výraz, který něco udělá podle hodnot <code>a</code> a <code>b</code> +a výsledek dá k dispozici, <code>len(slovo)</code> je výraz, který něco udělá +podle hodnoty <code>slovo</code> a výsledek dá k dispozici.</p> +<p>Vždycky, když Python při vyhodnocování narazí na jméno funkce se závorkami, +funkci <em>zavolá</em>, zjistí výsledek a dosadí ho:</p> +<div class="highlight"><pre><span></span><span class="n">vysledek</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="s2">"Ahoj!"</span><span class="p">)</span> +<span class="c1"># ╰────┬─────╯</span> +<span class="n">vysledek</span> <span class="o">=</span> <span class="mi">5</span> +</pre></div><p>Volání funkce můžeš kombinovat s jinými výrazy, třeba se součtem:</p> +<div class="highlight"><pre><span></span><span class="n">delka</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="s1">'Ahoj'</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="s1">'!'</span><span class="p">)</span> +<span class="c1"># ╰──┬─────╯ ╰─┬───╯</span> +<span class="n">delka</span> <span class="o">=</span> <span class="mi">4</span> <span class="o">+</span> <span class="mi">1</span> +<span class="c1"># ╰───────┬────╯</span> +<span class="n">delka</span> <span class="o">=</span> <span class="mi">5</span> +</pre></div><p>Nebo v podmínce ifu – třeba u:</p> +<div class="highlight"><pre><span></span><span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="s1">'Ahoj!'</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">3</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'pozdrav je krátký'</span><span class="p">)</span> +</pre></div><p>… se za <code>len('Ahoj!') <= 3</code> nakonec dosadí nepravda (<code>False</code>):</p> +<div class="highlight"><pre><span></span> <span class="nb">len</span><span class="p">(</span><span class="s1">'Ahoj!'</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">3</span> +<span class="c1"># ╰─────┬────╯</span> + <span class="mi">5</span> <span class="o"><=</span> <span class="mi">3</span> +<span class="c1"># ╰──────┬──╯</span> + <span class="kc">False</span> +</pre></div><p>Volání funkce můžeš použít i jako argument pro jinou funkci:</p> +<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="s1">'Ahoj'</span><span class="p">))</span> +<span class="c1"># ╰────┬────╯</span> +<span class="nb">print</span><span class="p">(</span> <span class="mi">4</span> <span class="p">)</span> <span class="c1"># vypíše 4</span> +</pre></div><p>Nebo to zkombinovat dohromady:</p> +<div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="mi">5</span> +<span class="nb">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="s1">'Ahoj'</span><span class="p">)</span> <span class="o">+</span> <span class="n">x</span><span class="p">)</span> +<span class="c1"># ╰────┬────╯ |</span> +<span class="nb">print</span><span class="p">(</span> <span class="mi">4</span> <span class="o">+</span> <span class="mi">5</span><span class="p">)</span> +<span class="c1"># ╰───┬────╯</span> +<span class="nb">print</span><span class="p">(</span> <span class="mi">9</span> <span class="p">)</span> +</pre></div><p>… a podobně.</p> +<h3 id="procedury">Procedury +<a href="#procedury" class="header-link">#</a> +</h3> +<p>Možná sis všiml/a, že jednu funkci už voláš déle: <code>print("Ahoj!")</code> +je taky volání funkce. +Stejně jako <code>len</code> dostává <code>print</code> v závorkách argument – hodnotu, se +kterou pracuje. +Liší se ale návratovou hodnotou.</p> +<p>Funkce <code>print</code> sice něco <em>udělá</em> – vypíše text +na obrazovku – ale nevrátí žádný smysluplný výsledek, který by zbytek programu +mohl dál zpracovat.</p> +<p>Funkcím, které nic nevrací (jen něco udělají), se občas říká <em>procedury</em>. +V Pythonu není hranice mezi „normální“ funkcí a procedurou příliš ostrá, +ale přesto se hodí tento koncept znát. +Pár příkladů:</p> +<ul> +<li>Funkce, která vybere náhodné číslo, je „normální“. +Svůj výsledek vrátí; program s ním může dál pracovat.</li> +<li>Funkce, která vykreslí na obrazovku kolečko, je <em>procedura</em>. +Žádnou zajímavou hodnotu programu nevrací.</li> +<li>Funkce, která spočítá průměrný věk obyvatelstva podle informací ze sčítání +lidu, je „normální“. Svůj výsledek vrátí a program s ním může dál pracovat.</li> +<li>Funkce, která přehraje písničku reproduktorem, je <em>procedura</em>. +Nic zajímavého programu nevrací.</li> +</ul> +<div class="admonition note"><p>Na rozdíl od ostatních termínů, které se tu učíš, není +„procedura“ v Pythonu zavedený pojem. +Je vypůjčený z jazyka Pascal. +Kdybys o něm diskutoval/a s nějakým zkušeným programátorem, +odkaž ho prosím na tyto materiály.</p> +</div><h2 id="argumenty">Argumenty +<a href="#argumenty" class="header-link">#</a> +</h2> +<p>Argument je to, co funkci dáš k dispozici. Hodnota, se kterou funkce pracuje. +Chceš-li délku řetězce <code>Ahoj!</code>, použiješ funkci <code>len</code>, která umí vypočítat +délku <em>jakéhokoli</em> řetězce, a jako argument, v závorkách, jí dáš tu svoji +konkrétní hodnotu: <code>len('Ahoj!')</code>.</p> +<p>Podobně funkce <code>print</code> umí vypsat jakoukoli hodnotu. +Tu, kterou má vypsat ve tvém konkrétním případě, jí předáš jako argument.</p> +<p>Některým funkcím můžeš předat i více argumentů. +Třeba zrovna funkci <code>print</code>, která všechny své argumenty vypíše na řádek. +Jednotlivé argumenty se oddělují čárkami:</p> +<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> +</pre></div><div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s2">"Jedna plus dva je"</span><span class="p">,</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span><span class="p">)</span> +</pre></div><p>Některé funkce nepotřebují žádný argument. +Příkladem je zase <code>print</code>. +Je ale nutné použít závorky – i když jsou prázdné. +Hádej, co tohle volání udělá?</p> +<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">()</span> +</pre></div><div class="solution" id="solution-0"> + <h3>Řešení</h3> + <div class="solution-cover"> + <a href="naucse:solution?solution=0"><span class="link-text">Ukázat řešení</span></a> + </div> + <div class="solution-body" aria-hidden="true"> + <p>Funkce <code>print</code> zavolaná bez argumentů napíše prázdný řádek.</p> + </div> +</div> + +<h3 id="pojmenovane_argumenty">Pojmenované argumenty +<a href="#pojmenovane_argumenty" class="header-link">#</a> +</h3> +<p>Některé funkce umí pracovat i s <em>pojmenovanými</em> argumenty. +Píšou se podobně jako přiřazení do proměnné, +s rovnítkem, ale uvnitř závorek.</p> +<p>Třeba funkce <code>print</code> při výpisu odděluje jednotlivé argumenty mezerou, +ale pomocí argumentu <code>sep</code> se dá použít i něco jiného.</p> +<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="n">sep</span><span class="o">=</span><span class="s1">', '</span><span class="p">)</span> <span class="c1"># Místo mezery odděluj čárkou</span> +</pre></div><p>Dá se změnit i to, co <code>print</code> udělá na konci výpisu. +Normálně přejde na nový řádek, ale argumentem <code>end</code> můžeš říct, co se má vypsat +<em>místo toho</em>.</p> +<div class="admonition note"><p>Tenhle příklad je potřeba napsat do souboru; v interaktivní konzoli +nebude výstup vypadat tak, jak má.</p> +</div><div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s1">'1 + 2'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s1">' '</span><span class="p">)</span> <span class="c1"># Místo přechodu na nový řádek jen napiš mezeru</span> +<span class="nb">print</span><span class="p">(</span><span class="s1">'='</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s1">' '</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s1">'!'</span><span class="p">)</span> +<span class="nb">print</span><span class="p">()</span> +</pre></div><h3 id="funkce_je_potreba_volat">Funkce je potřeba volat +<a href="#funkce_je_potreba_volat" class="header-link">#</a> +</h3> +<p>Pozor na to, že když nenapíšeš závorky, funkce se nezavolá! +Výraz <code>len(s)</code> je <em>volání funkce</em>, ale <code>len</code> bez závorek označuje +<em>funkci samotnou</em>.</p> +<p>Výsledek výrazu <code>len(s)</code> je číslo; výsledek výrazu <code>len</code> je samotná funkce <code>len</code>.</p> +<p>Čísla můžeš sčítat, můžeš tedy napsat <code>len(s) + 1</code>. +Funkce ale sčítat nejde – <code>len + 1</code> nedává smysl.</p> +<p>Často se ale stane, že závorky prostě zapomeneš. +Zkus si, co dělají následující příklady, a pozorně si přečti výsledky +a chybové hlášky, abys pak podobné chyby poznal/a:</p> +<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="s1">'a'</span><span class="p">))</span> <span class="c1"># Volání funkce (a vypsání výsledku)</span> +<span class="nb">print</span><span class="p">(</span><span class="nb">len</span><span class="p">)</span> <span class="c1"># Vypsání samotné funkce</span> +<span class="nb">print</span><span class="p">(</span><span class="nb">len</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># Sečtení funkce a čísla</span> +</pre></div><h2 id="prehled_funkci">Přehled funkcí +<a href="#prehled_funkci" class="header-link">#</a> +</h2> +<p>A jaké funkce můžeš, kromě <code>len</code> a <code>print</code>, použít? +Přehled těch základních najdeš v <a href="naucse:page?lesson=beginners/basic-functions">následující lekci</a>.</p> \ No newline at end of file diff --git a/lessons/generators/index.html b/lessons/generators/index.html new file mode 100644 index 00000000..b1fd83e2 --- /dev/null +++ b/lessons/generators/index.html @@ -0,0 +1,254 @@ +<h1 id="generatory">Generátory +<a href="#generatory" class="header-link">#</a> +</h1> +<p>Dnes si popíšeme, jak v Pythonu fungují <em>generátory</em>, tedy funkce s příkazem <code>yield</code>. +Někteří z vás možná už nějaký jednoduchý generátor napsali, ale pojďme si je +vysvětlit od úplného začátku: od toho, jak se v Pythonu iteruje.</p> +<h2 id="iterace">Iterace +<a href="#iterace" class="header-link">#</a> +</h2> +<p>Když je v Pythonu potřeba iterovat (např. příkazem <code>for</code>) přes nějakou kolekci +(seznam, řetězec, soubor, …), použije se <em>iterační protokol</em>, +který pracuje se dvěma druhy objektů: s <em>iterovatelnými objekty</em> a s <em>iterátory</em>.</p> +<p>Iterovatelné objekty (<em>iterables</em>) se vyznačují tím, že je na ně možné zavolat +funkci <code>iter()</code>. Ta vrátí příslušný iterátor:</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">iter</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span> +<span class="go"><list_iterator object at 0x...></span> +</pre></div><p>Na iterátor pak je možné opakovaně volat funkci <code>next()</code>, čímž dostáváme jednotlivé +prvky iterace. +Po vyčerpání iterátoru způsobuje <code>next()</code> výjimku <code>StopIteration</code>:</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">it</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="go">1</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="go">2</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="go">3</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="gt">Traceback (most recent call last):</span> +<span class="w"> </span><span class="c">...</span> +<span class="gr">StopIteration</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="gt">Traceback (most recent call last):</span> +<span class="w"> </span><span class="c">...</span> +<span class="gr">StopIteration</span> +</pre></div><p>Cyklus <code>for x in collection: ...</code> tedy dělá něco jako:</p> +<div class="highlight"><pre><span></span><span class="n">iterator</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">collection</span><span class="p">)</span> +<span class="k">while</span> <span class="kc">True</span><span class="p">:</span> + <span class="k">try</span><span class="p">:</span> + <span class="n">x</span> <span class="o">=</span> <span class="nb">next</span><span class="p">(</span><span class="n">iterator</span><span class="p">)</span> + <span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span> + <span class="k">break</span> + + <span class="o">...</span> <span class="c1"># tělo původního cyklu</span> +</pre></div><p>Platí, že každý iterátor je iterovatelný: zavoláním <code>iter()</code> na iterátor +dostaneme ten stejný iterátor (nikoli jeho kopii) zpět. +Naopak to ale obecně neplatí: např. seznamy jsou iterovatelné, ale nejsou samy +o sobě iterátory. +Každé zavolání <code>iter(seznam)</code> vrací nový iterátor, který má svou vlastní +„aktuální pozici“ a iteruje od začátku.</p> +<p>Iterátor je ve většině případů „malý“ objekt, který si „pamatuje“ jen původní iterovatelný +objekt a aktuální pozici. Příklady jsou iterátory seznamů (<code>iter([])</code>), slovníků (<code>iter({})</code>), +<em>n</em>-tic nebo množin, iterátor pro <code>range</code> a podobně.</p> +<p>Iterátory ale můžou být i „větší“: třeba otevřený soubor je iterátor, z něhož <code>next()</code> +načítá jednotlivé řádky.</p> +<h2 id="generatory">Generátory +<a href="#generatory" class="header-link">#</a> +</h2> +<p>Asi nejzajímavější druh iterátoru je tzv. <em>generátor</em>: funkce, která umí postupně +dávat k dispozici hodnoty. +Definuje se pomocí klíčového slova <code>yield</code>: každá funkce, která obsahuje <code>yield</code>, +je <em>generátorová funkce</em> (angl. <em>generator function</em>).</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">generate2</span><span class="p">():</span> +<span class="w"> </span><span class="sd">"""generates 2 numbers"""</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'A'</span><span class="p">)</span> + <span class="k">yield</span> <span class="mi">0</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'B'</span><span class="p">)</span> + <span class="k">yield</span> <span class="mi">1</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'C'</span><span class="p">)</span> +</pre></div><p>Zavoláním takové funkce dostáváme <em>generátorový iterátor</em> (angl. <em>generator iterator</em>):</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">generate2</span><span class="p">()</span> +<span class="go"><generator object generate2 at 0x...></span> +</pre></div><p>Voláním <code>next()</code> se pak stane zajímavá věc: funkce se provede až po první <code>yield</code>, +tam se <em>zastaví</em> a hodnota <code>yield</code>-u se vrátí z <code>next()</code>. +Při dalším volání se začne provádět zbytek funkce od místa, kde byla naposled +zastavena.</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">it</span> <span class="o">=</span> <span class="n">generate2</span><span class="p">()</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="go">A</span> +<span class="go">0</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="go">B</span> +<span class="go">1</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="go">C</span> +<span class="gt">Traceback (most recent call last):</span> +<span class="w"> </span><span class="c">...</span> +<span class="gr">StopIteration</span> +</pre></div><h2 id="dalsi_pouziti_generatoru">Další použití generátorů +<a href="#dalsi_pouziti_generatoru" class="header-link">#</a> +</h2> +<p>Vlastnost přerušit provádění funkce je velice užitečná nejen pro vytváření +sekvencí, ale má celou řadu dalších užití. +Existuje třeba dekorátor, který generátorovou funkci s jedním <code>yield</code> převede na <em>context manager</em>, +tedy objekt použitelný s příkazem <code>with</code>:</p> +<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">contextlib</span> + +<span class="nd">@contextlib</span><span class="o">.</span><span class="n">contextmanager</span> +<span class="k">def</span> <span class="nf">ctx_manager</span><span class="p">():</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'Entering'</span><span class="p">)</span> + <span class="k">yield</span> <span class="mi">123</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'Exiting'</span><span class="p">)</span> + + +<span class="k">with</span> <span class="n">ctx_manager</span><span class="p">()</span> <span class="k">as</span> <span class="n">obj</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'Inside context, with'</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span> +</pre></div><p>Vše před <code>yield</code> se provede při vstupu do kontextu, hodnota <code>yield</code> se předá +dál a vše po <code>yield</code> se provede na konci. +Můžeme si představit, že místo <code>yield</code> se „doplní“ obsah bloku <code>with</code>. +Funkce se tam na chvíli zastaví a může se tedy provádět něco jiného.</p> +<h2 id="vraceni_hodnot_z_generatoru">Vracení hodnot z generátorů +<a href="#vraceni_hodnot_z_generatoru" class="header-link">#</a> +</h2> +<p>V rámci generátorové funkce můžeme použít i <code>return</code>, který funkci ukončí. +Vrácená hodnota se však při normální iteraci (např. ve <code>for</code>) nepoužije. +Objeví se pouze jako hodnota výjimky <code>StopIteration</code>, která signalizuje konec +iterace:</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">generator</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> +<span class="w"> </span><span class="sd">"""Yield two numbers and return their sum"""</span> + <span class="k">yield</span> <span class="n">a</span> + <span class="k">yield</span> <span class="n">b</span> + <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span> +</pre></div><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">it</span> <span class="o">=</span> <span class="n">generator</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="go">2</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="go">3</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="gt">Traceback (most recent call last):</span> + File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></span> +<span class="gr">StopIteration</span>: <span class="n">5</span> +</pre></div><h2 id="obousmerna_komunikace">Obousměrná komunikace +<a href="#obousmerna_komunikace" class="header-link">#</a> +</h2> +<p>Oproti normálním iterátorům, které hodnoty jen poskytují, mají generátory metodu +<code>send()</code>, kterou je možné posílat hodnoty <em>do</em> běžícího generátoru. +Klíčové slovo <code>yield</code> totiž může fungovat jako výraz a tento výraz nabývá poslanou +hodnotu (nebo <code>None</code>, byl-li použit normální <code>next()</code>).</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">running_sum</span><span class="p">():</span> + <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span> + <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> + <span class="n">num</span> <span class="o">=</span> <span class="p">(</span><span class="k">yield</span> <span class="n">total</span><span class="p">)</span> + <span class="k">if</span> <span class="n">num</span><span class="p">:</span> + <span class="n">total</span> <span class="o">+=</span> <span class="n">num</span> + +<span class="n">it</span> <span class="o">=</span> <span class="n">running_sum</span><span class="p">()</span> +<span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="c1"># pro první iteraci nelze použít send() -- nečekáme zatím na yield-u</span> +<span class="n">it</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> +<span class="n">it</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> +<span class="k">assert</span> <span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="o">==</span> <span class="mi">5</span> +</pre></div><p>Upřímě řečeno, metoda <code>send()</code> není příliš užitečná. +(Když byste něco takového potřebovali, radši si napište třídu, která si bude +stav uchovávat v atributech, a měňte ji třeba metodami. Bude to pravděpodobně +přehlednější.) +Existuje ale příbuzná metoda, která už je užitečnější: <code>throw()</code>. +Ta do generátoru „vhodí“ výjimku. +Z pohledu generátorové funkce to vypadá, jako by výjimka nastala na příkazu +<code>yield</code>.</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">report_exception</span><span class="p">():</span> + <span class="k">try</span><span class="p">:</span> + <span class="k">yield</span> + <span class="k">except</span> <span class="ne">BaseException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'Death by'</span><span class="p">,</span> <span class="nb">type</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span> + <span class="k">yield</span> <span class="mi">123</span> +</pre></div><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">it</span> <span class="o">=</span> <span class="n">report_exception</span><span class="p">()</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="c1"># opět – v první iteraci nelze throw() použít</span> +<span class="gp">>>> </span><span class="n">value</span> <span class="o">=</span> <span class="n">it</span><span class="o">.</span><span class="n">throw</span><span class="p">(</span><span class="ne">ValueError</span><span class="p">())</span> +<span class="go">Death by ValueError</span> +<span class="gp">>>> </span><span class="n">value</span> +<span class="go">123</span> +</pre></div><p>Podobná věc se děje, když generátorový iterátor zanikne: Python do generátoru +„vhodí“ výjimku GeneratorExit. +Ta dědí z <code>BaseException</code>, ale ne <code>Exception</code>, takže klasické <code>except Exception:</code> +ji nechytí (ale např. <code>finally</code> funguje jak má). +Pokud generátor tuto výjimku chytá, měl by se co nejdřív ukončit. +(Když to neudělá a provede další <code>yield</code>, Python ho ukončí „násilně“.)</p> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">gc</span> +<span class="gp">>>> </span><span class="n">it</span> <span class="o">=</span> <span class="n">report_exception</span><span class="p">()</span> +<span class="gp">>>> </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> +<span class="gp">>>> </span><span class="k">del</span> <span class="n">it</span><span class="p">;</span> <span class="n">gc</span><span class="o">.</span><span class="n">collect</span><span class="p">()</span> <span class="c1"># zbavíme se objektu "it"</span> +<span class="go">Death by GeneratorExit</span> +<span class="go">Exception ignored in: <generator object report_exception at 0x...></span> +<span class="go">RuntimeError: generator ignored GeneratorExit</span> +<span class="go">0</span> +</pre></div><h2 id="delegace_na_podgenerator_codeyield_fromcode">Delegace na podgenerátor – <code>yield from</code> +<a href="#delegace_na_podgenerator_codeyield_fromcode" class="header-link">#</a> +</h2> +<p>Máme následující generátor:</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">dance</span><span class="p">():</span> + <span class="k">yield</span> <span class="s1">'putting hands forward'</span> + <span class="k">yield</span> <span class="s1">'putting hands down'</span> + <span class="k">yield</span> <span class="s1">'turning around'</span> + <span class="k">yield</span> <span class="s1">'jumping'</span> + <span class="k">yield</span> <span class="s1">'putting hands forward'</span> + <span class="k">yield</span> <span class="s1">'putting hands down'</span> + +<span class="k">for</span> <span class="n">action</span> <span class="ow">in</span> <span class="n">dance</span><span class="p">():</span> + <span class="nb">print</span><span class="p">(</span><span class="n">action</span><span class="p">)</span> +</pre></div><p>Opakuje se v něm jistá sekvence, kterou bychom jako správní programátoři chtěli +vyčlenit do samostatné funkce. +Pomocí samotného <code>yield</code> to ale jde celkem těžko:</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">dance_hands</span><span class="p">():</span> + <span class="k">yield</span> <span class="s1">'putting hands forward'</span> + <span class="k">yield</span> <span class="s1">'putting hands down'</span> + +<span class="k">def</span> <span class="nf">dance</span><span class="p">():</span> + <span class="k">for</span> <span class="n">action</span> <span class="ow">in</span> <span class="n">dance_hands</span><span class="p">():</span> + <span class="k">yield</span> <span class="n">action</span> + <span class="k">yield</span> <span class="s1">'turning around'</span> + <span class="k">yield</span> <span class="s1">'jumping'</span> + <span class="k">for</span> <span class="n">action</span> <span class="ow">in</span> <span class="n">dance_hands</span><span class="p">():</span> + <span class="k">yield</span> <span class="n">action</span> + +<span class="k">for</span> <span class="n">action</span> <span class="ow">in</span> <span class="n">dance</span><span class="p">():</span> + <span class="nb">print</span><span class="p">(</span><span class="n">action</span><span class="p">)</span> +</pre></div><p>Tohle počtu řádků příliš nepomohlo. Existuje lepší způsob – místo cyklu +můžeme použít <code>yield from</code>:</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">dance_hands</span><span class="p">():</span> + <span class="k">yield</span> <span class="s1">'putting hands forward'</span> + <span class="k">yield</span> <span class="s1">'putting hands down'</span> + +<span class="k">def</span> <span class="nf">dance</span><span class="p">():</span> + <span class="k">yield from</span> <span class="n">dance_hands</span><span class="p">()</span> + <span class="k">yield</span> <span class="s1">'turning around'</span> + <span class="k">yield</span> <span class="s1">'jumping'</span> + <span class="k">yield from</span> <span class="n">dance_hands</span><span class="p">()</span> + +<span class="k">for</span> <span class="n">action</span> <span class="ow">in</span> <span class="n">dance</span><span class="p">():</span> + <span class="nb">print</span><span class="p">(</span><span class="n">action</span><span class="p">)</span> +</pre></div><p>Navíc lze <code>yield from</code> použít jako výraz, který nabývá hodnoty +kterou podgenerátor vrátil (t.j. hodnota výjimky <code>StopIteration</code>).</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">limit</span><span class="p">):</span> + <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span> + <span class="n">b</span> <span class="o">=</span> <span class="mi">1</span> + <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span> + <span class="k">while</span> <span class="n">b</span> <span class="o"><</span> <span class="n">limit</span><span class="p">:</span> + <span class="k">yield</span> <span class="n">b</span> + <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span> + <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span> + <span class="k">return</span> <span class="n">count</span> + +<span class="k">def</span> <span class="nf">fib_annotated</span><span class="p">():</span> + <span class="n">count</span> <span class="o">=</span> <span class="p">(</span><span class="k">yield from</span> <span class="n">fibonacci</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'LOG: Generated </span><span class="si">{}</span><span class="s1"> numbers'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">count</span><span class="p">))</span> + +<span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">fib_annotated</span><span class="p">():</span> + <span class="nb">print</span><span class="p">(</span><span class="s1">'Got'</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> +</pre></div><div class="highlight"><pre><code>Got 1 +Got 1 +Got 2 +Got 3 +Got 5 +Got 8 +LOG: Generated 6 numbers +</code></pre></div><p>Kromě toho <code>yield from</code> deleguje hodnoty poslané pomocí <code>send()</code> či <code>throw()</code>.</p> \ No newline at end of file diff --git a/lessons/git/git-collaboration-2in1/static/diagram.png b/lessons/git-collaboration-2in1/diagram.png similarity index 100% rename from lessons/git/git-collaboration-2in1/static/diagram.png rename to lessons/git-collaboration-2in1/diagram.png diff --git a/lessons/git/git-collaboration-2in1/static/gh-workflow-diagram.svg b/lessons/git-collaboration-2in1/gh-workflow-diagram.svg similarity index 100% rename from lessons/git/git-collaboration-2in1/static/gh-workflow-diagram.svg rename to lessons/git-collaboration-2in1/gh-workflow-diagram.svg diff --git a/lessons/git-collaboration-2in1/index.html b/lessons/git-collaboration-2in1/index.html new file mode 100644 index 00000000..746e95f7 --- /dev/null +++ b/lessons/git-collaboration-2in1/index.html @@ -0,0 +1,249 @@ +<h1 id="spoluprace_a_git">Spolupráce a Git +<a href="#spoluprace_a_git" class="header-link">#</a> +</h1> +<p>„Opravdové” programy zřídka vznikají prací jednoho člověka. Víc hlav víc ví, a tak je dobré si na projekt vytvořit tým. +Každý člen týmu potřebuje mít přístup k práci ostatních. Jak ale zařídit to, aby několik lidí mohlo najednou upravovat +stejné části (soubory) programu?</p> +<p>Určitě jste někdy použili nějaký nástroj na sdílení souborů jako je Dropbox nebo OneDrive. +Tyto nástroje se většinou moc neumí vyrovnat s tím, když se změní jeden soubor najednou na dvou počítačích. +Většinou vytvoří 2 konfliktní kopie a je na uživatelích, aby našli v souborech změny a spojili je.</p> +<p>Takový způsob práce není ve velkém projektu s mnoha programátory udržitelný. +Kód je většinou v mnoha souborech a přidání jedné nové funkcionality může vyžadovat změnu ve více souborech. +Jen si představte jakou paseku by nadělalo pět programátorů v jedné Dropbox složce.</p> +<p>Zdrojový kód se dá poměrně dobře sdílet a přepoužívat, pokud je napsaný podle určitých pravidel. +Spolupráce a sdílení kódu je spjato s programováním od nepaměti, už od prvních počítačů. +Tohle přepoužívání se postupně vyvinulo do fenoménu <em>open-source</em> a <em>svobodného softwaru</em>.</p> +<p>Dnes si vyzkoušíme, jak spolupracovat s mnoha programátory na jednom projektu pomocí verzovacího nástroje <em>Git</em> a služby <em>GitHub</em>.</p> +<h2 id="git_a_github">Git a GitHub +<a href="#git_a_github" class="header-link">#</a> +</h2> +<p>Představte si, že jste už napsali větší Python program, který je už kvůli složitosti a velikosti rozdělen do více souborů. +Takové složce s kódem říkáme repozitář. +Git je program, který se stará o to, abyste ve složce mohli dělat různé experimenty a vrátit se třeba ke starým verzím. +Každá verze má ale popis, který jste jí sami dali, takže slouží i jako dobrá dokumentace projektu.</p> +<p>A co víc – Git vám dovoluje sdílet kód s ostatními programátory. +Spousta míst na Internetu funguje tak, že vybraná skupina lidí má „přístup”: můžou dělat změny, jak se jim líbí. +S Gitem se používá jiný model: změny nahrajeme do vlastního sdíleného repozitáře +a majiteli původního projektu napíšeme žádost o začlenění těch změn (angl. pull request). +Může to být třeba mail se slovy „Hele, na té a té adrese mám nějaké změny, které by se ti mohly hodit! Přidej je do svého projektu!”</p> +<p>Výhoda je v tom, že se do projektu ‒ pokud je veřejný ‒ může zapojit kdokoliv. +Přispěvatel se nemusí předem ptát, nemusí dokazovat že je důvěryhodná osoba, stačí něco změnit a poslat. +Jestli se změna bude autorům projektu líbit nebo ne, to už je jiná věc. +Ale záleží hlavně na samotné změně, ne na tom, kdo ji udělal. +Většina projektů v README obsahuje návod, jak do projektu přispět (angl. Contribution Guidelines). +Zkuste se po nich podívat, ušetříte práci sobě i autorům.</p> +<p>Služba <a href="https://github.com">GitHub</a> umožňuje vytvořit si vlastní sdílený gitový repozitář a zjednodušuje začleňování změn +(místo posílání mailů stačí zmáčknout tlačítko).</p> +<p>Podobných služeb existuje víc (např. bitbucket.org, gitlab.com). Všechny fungují podobně; GitHub je momentálně nejpopulárnější.</p> +<p>Kdybyste měli v různých kopiích repozitáře zmatek, přijde vhod malé vysvětlení: jedna kopie je původní projekt na GitHubu, +kam správce projektu dává aktuální „oficiální verzi”. +Další kopie na GitHubu je „vaše” a můžete si do ní nahrát cokoliv +(nejčastěji se v ní ale zveřejňují změny, které můžou být užitečné pro ostatní). +A třetí kopii repozitáře máte u sebe na počítači.</p> +<p><img src="naucse:static?filename=gh-workflow-diagram.svg" alt="Git workflow" /></p> +<h3 id="prace_s_lokalnim_repozitarem">Práce s lokálním repozitářem +<a href="#prace_s_lokalnim_repozitarem" class="header-link">#</a> +</h3> +<p>Než budeme schopní poslat nějaké změny k začlenění do našeho vybraného projektu, musíme se naučit, jak se s Gitem pracuje. +Co je to vlastně ten zmíněný <em>repozitář</em>? Můžeme ho vytvořit z jakéhokoliv adresáře na disku, kde máme uložené soubory, +jen Gitu řekneme, že z něj chceme ten repozitář udělat. To má za následek:</p> +<ol> +<li>Git bude sledovat změny, které ve složce uděláme.</li> +<li>Budeme schopni vytvořit verzi tohoto adresáře, ke které se můžeme kdykoliv vrátit.</li> +<li>Můžeme nastavit vzdálené repozitáře, kam chceme změny nahrávat nebo naopak stahovat změny od jiných lidí.</li> +</ol> +<div class="admonition note"><p class="admonition-title">Pozor</p> +<p>Budeme hodně pracovat s příkazovou řádkou. Jestli se s ní ještě nekamarádíš, koukni se na <a href="naucse:page?lesson=beginners/cmdline">úvod</a>. +Nezapomeň: $ na začátku se nepíše; je tu proto, aby šlo poznat, že jde o příkaz.</p> +</div><p>Naším dnešním cílem je ale přispět do projektu, který založil někdo jiný. Proto použijeme příkaz <code>git clone</code> ke stažení vzdáleného repozitáře.</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>clone<span class="w"> </span>https://github.com/asgeirrr/prezencka +</pre></div><p>V aktuálním adresáři se nám vytvořil podadresář <code>prezencka</code>, který obsahuje všechny soubory, které vidíme ve webovém rozhraní na <a href="https://github.com/asgeirrr/prezencka">GitHubu</a>.</p> +<h4 id="stav_repozitare_codegit_statuscode">Stav repozitáře (<code>git status</code>) +<a href="#stav_repozitare_codegit_statuscode" class="header-link">#</a> +</h4> +<p>Velmi často potřebujeme zjistit současný stav repozitáře. Navigujte v příkazové řádce do adresáře <code>prezencka</code> a zadejte <code>git status</code>. Git nám odpoví:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>status +<span class="go">On branch master</span> +<span class="go">Your branch is up-to-date with 'origin/master'.</span> +<span class="go">nothing to commit, working tree clean</span> +</pre></div><p>Git nám v tuto chvíli poskytl 3 informace:</p> +<ol> +<li>Že jsme na větvi <code>master</code>. Git umožňuje pracovat na více věcích současně pomocí tzv. <em>větví</em> mezi kterými můžeme přepínat. Pak se na na disku „objeví” verze souborů v dané větvi. My si dnes vystačíme s jednou hlavní větví, která se tradičně jmenuje <code>master</code>.</li> +<li>Že naše větev <code>master</code> je aktuální vůči větvi na vzdáleném repozitáři (origin) na GitHubu.</li> +<li>Že jsme neudělali žádné změny oproti poslední verzi souborů, což je pravda.</li> +</ol> +<h4 id="prvni_revize_codegit_commitcode">První revize (<code>git commit</code>) +<a href="#prvni_revize_codegit_commitcode" class="header-link">#</a> +</h4> +<p>Už jsme několikrát zmínili, že Git je verzovací systém. Jak že to ale soubory verzuje? +V podstatě mu musíme dát příkaz, aby zmrazil současný stav repozitáře, tzv. vytvořit <em>revizi</em>. +Pak můžeme dělat další změny, ale už vždy budeme schopni se k této revizi vrátit.</p> +<p>Přidejte do adresáře <code>prezencka</code> soubor <code>vase_jmeno.txt</code>, např. tedy <code>magdalena_rettigova.txt</code> +a do něj napište svoje současné povolání nebo cokoliv jiného. +Zkontrolujte současný stav repozitáře pomocí <code>git status</code>: Git oznámí, že v adresáři je soubor, o kterém ještě „neví“.</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>status +<span class="go">On branch master</span> +<span class="go">Your branch is up-to-date with 'origin/master'.</span> +<span class="go">Untracked files:</span> +<span class="go"> (use "git add <file>..." to include in what will be committed)</span> + +<span class="go"> magdalena_rettigova.txt</span> + +<span class="go">nothing added to commit but untracked files present (use "git add" to track)</span> +</pre></div><p>U každého nového souboru musíme Gitu říct, že chceme jeho obsah sledovat. Proveďte to se svým souborem:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>add<span class="w"> </span>magdalena_rettigova.txt +</pre></div><p>a znovu zkontrolujte stav repozitáře:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>status +<span class="go">On branch master</span> +<span class="go">Your branch is up-to-date with 'origin/master'.</span> +<span class="go">Changes to be committed:</span> +<span class="go"> (use "git reset HEAD <file>..." to unstage)</span> + +<span class="go"> new file: magdalena_rettigova.txt</span> +</pre></div><p>To, co je zelené („changes to be committed“), se přidá do další revize (angl. commit), kterou vytvoříme. Pojďme tedy vytvořit revizi:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>commit +</pre></div><p>Po zadání tohoto příkazu se otevře editor, do kterého je nutné napsat výstižný popis změn, +abyste vy i ostatní programátoři věděli, co tahle revize obsahuje za změny. +Např. tedy <code>Přidáno mé jméno a povolání</code>. Předvyplněné řádky začínající # můžeme nechat být +(nebo vymazat, podle chuti – Git je ignoruje). +Pak soubor uložíme a zavřeme editor.</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>status +<span class="go">On branch master</span> +<span class="go">Your branch is ahead of 'origin/master' by 1 commit.</span> +<span class="go"> (use "git push" to publish your local commits)</span> +<span class="go">nothing to commit, working tree clean</span> +</pre></div><p>Pro lepší pochopení, co dělají jednotlivé příkazy a v jakém stavu můžou být soubory/změny, přikládáme tento diagram:</p> +<p><img src="naucse:static?filename=diagram.png" alt="Git workflow" /></p> +<h4 id="log_codegit_logcode">Log (<code>git log</code>) +<a href="#log_codegit_logcode" class="header-link">#</a> +</h4> +<p>Teď, když za sebou máme první revizi, podívejme se, jak vypadá historie repozitáře, do kterého chceme přispívat. +K tomu můžeme použít příkaz <code>git log</code>, jako první uvidíme naši poslední změnu a další jsou změny od jiných autorů, +na které navazujeme.</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>log +<span class="go">commit 0bdfbb2a2398fea179395a8dd303e9f672ef4dca</span> +<span class="go">Author: Magdalena Dobromila <magdalena@rettigova.cz></span> +<span class="go">Date: Tue Mar 28 19:27:08 2017 +0200</span> + +<span class="go"> Přidáno mé jméno a povolání</span> + +<span class="go">commit 0f305972803131cb6c8637359fee8ede3005bba6</span> +<span class="go">Merge: effa89b 175f4cc</span> +<span class="go">Author: Oskar Hollmann <oskar@hollmann.me></span> +<span class="go">Date: Tue Nov 8 20:17:00 2016 +0100</span> + +<span class="go"> Merge pull request #1 from muzikovam/master</span> + +<span class="go"> Tady mas jmeno</span> +</pre></div><p>V logu se pohybujeme šipkami a když z něj chceme vyskočit zpět, stiskneme <code>q</code> jako quit. +Příkaz <code>git log</code> má mnoho přepínačů, přehlednější historii změn můžeme získat pomocí <code>git log --oneline --graph --decorate</code>.</p> +<h2 id="open-source_a_free_software">Open-source a Free software +<a href="#open-source_a_free_software" class="header-link">#</a> +</h2> +<p>Dejme si menší pauzu na zažití základní práce s lokálním repozitářem. +Nejde mluvit o Gitu a spolupráci a nezastavit se chvíli u otevřeného zdrojového kódu. +První kód vznikal v akademické sféře, kde byl zcela přirozeně sdílen, jako je to s poznatky mezi vědci běžné. +50. a 60. léta byla obdobím velké kreativity, kdy vzniklo mnoho z konceptů a technologií, které dnes používáme. +Pak se začalo programování postupně komercializovat a firmy začaly zdrojový kód skrývat jako konkurenční výhodu. +Do té doby víceméně jednolitá komunita programátorů byla nucena se rozdělit.</p> +<p>Některým programátorům tohle skrývání kódu hluboce vadilo až roku 1985 publikoval +<a href="https://en.wikipedia.org/wiki/Richard_Stallman">Richard Stallman</a> GNU Manifesto, +kde vysvětlil, proč hodlá vytvořit operační systém s otevřeným kódem a odstartoval tak hnutí svobodného softwaru. +To prosazuje 4 následujících svobody (převzato z <a href="https://cs.wikipedia.org/wiki/Svobodn%C3%BD_software">Wikipedie</a>):</p> +<ol> +<li>svoboda používat program za jakýmkoliv účelem,</li> +<li>svoboda studovat, jak program pracuje a možnost přizpůsobit ho svým potřebám,</li> +<li>svoboda redistribuovat kopie programu,</li> +<li>svoboda vylepšovat program a zveřejňovat zlepšení, aby z nich mohla mít prospěch celá komunita.</li> +</ol> +<p>Dnes je spousta projektů s otevřeným zdrojovým kódem dostupných na Internetu a každý je používáme. +Jejich další sdílení je upraveno jednou z licencí, které tyto základní svobody zaručují.</p> +<div class="admonition note"><p class="admonition-title">Pozor</p> +<p>Termíny <strong>open-source</strong> a <strong>free software</strong> nejsou zcela zaměnitelné, ale pro naše účely je zatím můžeme chápat jako synonyma.</p> +</div><p>Spoustu open-source projektů najdete právě na GitHubu. Ne všechny jsou v Pythonu. Ne všechny jsou kvalitní – +protože si každý může zveřejnit co chce, na GitHubu se válí spousta nedodělků, opuštěných nápadů a nepodařených experimentů. +A bohužel, ne všechny projekty mají přátelské autory.</p> +<p>Na druhou stranu ale open-source programy mají svoje výhody: nejenom se z nich může kdokoli učit, +ale každý může i zkontrolovat, jestli dělají to, co dělat mají. +Populární open-source programy nás například pravděpodobně nebudou špehovat (tj. hlásit autorovi, co na počítači děláme), +ani většinou neobsahují reklamy: kdyby to dělaly, najde se někdo kdo tyhle „funkce” odstraní +a lidé časem začnou používat opravenou verzi.</p> +<p>Některé příklady populárních open-source projektů:</p> +<ul> +<li>Mozilla Firefox, Chromium (prohlížeče)</li> +<li>Atom, gedit (textové editory)</li> +<li>CPython (jazyk Python)</li> +<li>Linux, Android (jádra operačních systémů)</li> +<li>Pytest (pythonní knihovna na testování)</li> +<li>Django, Flask, Requests (webové knihovny pro Python)</li> +<li>NumPy, Jupyter, Matplotlib (pythonní knihovny pro vědce a analytiky)</li> +<li>Materiály k tomuto kurzu</li> +</ul> +<p>A jak vidno z posledního příkladu, nejen softwarové projekty se dají vést takhle veřejně. Tento kurz vychází z principů open source: +všechno know-how je sdílené a budeme rádi, když se zapojíte.</p> +<p>Pokud vás tato problematika zajímá, doporučujeme krátkou knihu <a href="https://www.root.cz/knihy/katedrala-a-trziste/">Katedrála a tržiště</a>, která nabízí srovnání open-source s tradičním vývojem softwaru za zavřenými dveřmi.</p> +<p>Pokud budete někdy začínat nový projekt a budete chtít, aby z něj měl prospěch někdo další, +stojí za to uvolnit ho pod svobodnou licencí a dát třeba právě na GitHub. +Licence projektu většinou najdete v souboru <code>LICENCE</code>. +Na kód neuvolněný pod svobodnou licencí se automaticky vztahuje copyright a ostatní ho nemohou volně sdílet.</p> +<p>Informace o open-source licencích najdete např. na <a href="https://choosealicense.com">choosealicence.com</a>, případně <a href="https://creativecommons.org">creativecommons.org</a> a <a href="https://opensource.org">opensource.org</a>.</p> +<h2 id="spoluprace_na_projektu">Spolupráce na projektu +<a href="#spoluprace_na_projektu" class="header-link">#</a> +</h2> +<p>Nyní se posuneme ke sdílení revize, kterou jsme vytvořili, a ke stažení revizí od ostatních přispěvatelů.</p> +<h3 id="posilani_zmen_codegit_pushcode">Posílání změn (<code>git push</code>) +<a href="#posilani_zmen_codegit_pushcode" class="header-link">#</a> +</h3> +<p>Nyní stačí požádat autora projektu, aby naše změny začlenil do repozitáře na GitHubu +a všichni uživatelé projektu, na kterém pracujeme, budou mít užitek z našich změn. +Nemáme ale do původního repozitáře práva na zápis, musíme tedy poslat změny nejprve do naší kopie repozitáře, +vizte obrázek na začátku.</p> +<p>Udělejte si účet na GitHubu (jestli ho ještě nemáte) a pak jděte na adresu <a href="https://github.com/asgeirrr/prezencka/">prezencky</a>, +kterou jste použili pro <code>git clone</code>. +Vpravo nahoře je tlačítko „Fork”, klikněte na něj. Tím se na GitHubu vytvoří vaše kopie repozitáře: +adresa by měla být něco jako <code>https://github.com/tvojejmeno/prezencka</code>.</p> +<p>A teď, jak nahrát změny z našeho počítače na GitHub? Git si u každého repozitáře na našem počítači pamatuje adresy, +odkud se dají stahovat a kam se dají posílat změny. +Seznam těchto adres nám ukáže příkaz <code>git remote -v</code>. Třeba:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>remote<span class="w"> </span>-v +<span class="go">origin https://github.com/asgeirrr/prezencka (fetch)</span> +<span class="go">origin https://github.com/asgeirrr/prezencka (push)</span> +</pre></div><p>Tenhle výstup znamená, že pod zkratkou <em>origin</em> se schovává adresa, ze které jsme repozitář naklonovali.</p> +<p>Přidejme si podobnou zkratku pro vlastní repozitář na GitHubu. Můžeme ho pojmenovat např. <em>moje</em> +nebo svým uživatelským jménem na GitHubu, +aby nám bylo jasné, že je to ten náš, do kterého můžeme nahrávat změny.</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>remote<span class="w"> </span>add<span class="w"> </span>tvojejmeno<span class="w"> </span>https://github.com/tvojejmeno/prezencka +</pre></div><p>Zkontrolujme, že se nám to povedlo:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>remote<span class="w"> </span>-v +<span class="go">origin https://github.com/asgeirrr/prezencka (fetch)</span> +<span class="go">origin https://github.com/asgeirrr/prezencka (push)</span> +<span class="go">tvojejmeno https://github.com/tvojejmeno/prezencka (fetch)</span> +<span class="go">tvojejmeno https://github.com/tvojejmeno/prezencka (push)</span> +</pre></div><p>Tolik k nastavení ‒ <code>git remote add</code> stačí udělat jednou pro každý repozitář. Pak už můžeme změny nahrávat pomocí:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>push<span class="w"> </span>tvojejmeno<span class="w"> </span>master +</pre></div><p>což znamená: pošli na adresu uloženou pod zkratkou <code>tvojejmeno</code> větev <code>master</code>.</p> +<p>Funguje? Můžeme se podívat na <code>https://github.com/tvojejmeno/prezencka</code> v prohlížeči a ujistit se, že tam změny jsou.</p> +<h3 id="zadost_o_zacleneni_pull_request">Žádost o začlenění (pull request) +<a href="#zadost_o_zacleneni_pull_request" class="header-link">#</a> +</h3> +<p>Teď zbývá požádat autory původního projektu, aby změny z tvého sdíleného repozitáře přidali do svojí kopie. +GitHub na to má vlastní mechanismus zvaný <em>pull request</em> (žádost o začlenění).</p> +<p>Na stránce původního projektu (na adrese, kterou jsme použili na začátku pro <code>git clone</code>) +by mělo být oznámení o nově nahrané větvi s velkým zeleným tlačítkem <code>Compare & pull request</code>. +Po kliknutí na tlačítko můžeme doplnit popis toho, co tahle změna obnáší, a pak zmáčkneme další tlačítko.</p> +<p>Hotovo; teď je na autorech projektu, aby se na změny podívali a přijali ‒ nebo začali diskusi o tom, jak ji ještě vylepšit. +(Diskutovat se dá na stránce pull requestu nebo přes mail.)</p> +<div class="admonition note"><p class="admonition-title">Poznámka</p> +<p>Procházíte-li materiály z domu, musíte počkat, než si někdo žádosti všimne a začlení ji. +To může trvat i pár dní; kdyby to bylo přes týden, tak se zkuste se ozvat znovu.</p> +</div><h3 id="aktualizace_codegit_pullcode">Aktualizace (<code>git pull</code>) +<a href="#aktualizace_codegit_pullcode" class="header-link">#</a> +</h3> +<p>Když budou změny od všech účastníků začleněné, můžeme si aktualizovat repozitář, který máme u sebe na počítači. Příkaz</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>pull<span class="w"> </span>origin<span class="w"> </span>master +</pre></div><p>stáhne změny z větve „master” z adresy pod zkratkou „origin”. +Pomocí <code>git log</code> se můžeme podívat, jak se projekt mezitím vyvinul.</p> +<p>Kruh se uzavřel, jsme schopni začlenit do projektu vlastní změny a naopak si stáhnout změny od ostatních. +Až příště uvidíte chyby v materiálech nebo nešikovnou formulaci, zkuste to opravit a udělat pull request.</p> +<p>Git je velké téma a mohli bychom probírat větvení nebo řešení konfliktů a spoustu dalšího, +ale nechme si to na pokračovací srazy nebo workshop.</p> \ No newline at end of file diff --git a/lessons/git/basics/index.md b/lessons/git/basics/index.md deleted file mode 100644 index 1e609c3b..00000000 --- a/lessons/git/basics/index.md +++ /dev/null @@ -1,491 +0,0 @@ -# Git - -Ať už programuješ nebo píšeš dokumenty, stává se, -že vytvoříš několik verzí. -Tuhle chceš archivovat část, která už není potřeba, -tamhle chceš svoji práci poslat k ohodnocení, -nebo dokonce kolegům, kteří na ni spolupracují. -A když se verze začnou kupit, může být problém se v nich vyznat. - -Část těchto problémů řeší nástroje jako Dropbox či -Google Drive, se kterými ses možná již setkal{{a}}. -Tam můžeš například sdílet svůj dokument s dalšími -lidmi nebo se můžeš vrátit k dřívější verzi dokumentu, -když něco pokazíš a nemůžeš si vzpomenout, jak to bylo -předtím. Příklad toho, jak to může vypadat, je zde: - -{{ figure( - img=static('dropbox.png'), - alt="Verzovací Rozhraní služby Dropbox" -) }} - -V tomto rozhraní ale vidíš pouze verze *jednoho dokumentu* a navíc -nemůžeš tušit, ke které verzi se to vlastně chceš -vrátit. Nevidíš ani čím se jednotlivé verze liší. -Pro větší projekt by byl takový způsob práce -neefektivní. - -Programátoři proto používají mocnější nástroje na -správu verzí (angl. version control system. VCS). -Asi nejpopulárnější z nich je Git, se kterým -se teď seznámíme. - -> [note] -> Budeme hodně pracovat s příkazovou řádkou. -> Jestli se s ní ještě nekamarádíš, koukni se na -> [úvod]({{ lesson_url('beginners/cmdline') }}). -> -> Nezapomeň: `$` na začátku se nepíše; -> je tu proto, aby šlo poznat že jde o příkaz. - - -## Instalace - -Popis instalace Gitu najdeš -[zde]({{ lesson_url('git/install') }}). -Jestli jsi instalaci přeskočil{{a}}, projdi si ji teď. - - -## Repozitář - -Každý projekt, který budeš verzovat, musí mít pro sebe -vyhrazený adresář. -Vytvoř si tedy nový adresář a přepni se do něj (pomocí `cd`). -Pak vytvoř gitový <em>repozitář</em> (angl. repository) -pomocí příkazu `git init`: - -```ansi -␛[36m$␛[0m git init -Initialized empty Git repository in .../.git/ -``` - -Na první pohled to vypadá, že se nic nestalo. -Tenhle příkaz totiž vytvořil *skrytý* adresář -`.git`, do kterého uložil nějaké informace. -Přesvědč se příkazem `ls -a` (Linux) nebo `dir /a` (Windows). -Adresář `.git` je schovaný proto, že -ho spravuje Git a ty bys v něm neměl{{a}} nic měnit. - -V repozitáři zatím nic není. -Zkus to ověřit příkazem `git status`, který -vypisuje informace o stavu repozitáře: - -```ansi -␛[36m$␛[0m git status -On branch master - -No commits yet - -nothing to commit (create/copy files and use "git add" to track) -``` - -*„On branch master”* říká něco o větvích, k tomu se vrátíme později. -*„No commits yet”* říká, že zatím nemáš uloženou žádnou revizi. -A *„nothing to commit”* říká, že je adresář -prázdný – nejsou tu žádné soubory k verzování. - - -## První revize - -Teď si zkus do Gitu něco přidat! - -Vytvoř soubor `basnicka.txt` a napiš do něj -nějakou básničku. -Měla by mít aspoň pět řádků, ať pak máme s čím pracovat. -Pak zkus znovu `git status`: Git oznámí, -že v adresáři je soubor, o kterém ještě „neví“. - -<!-- XXX: Color coding! --> - -```ansi -␛[36m$␛[0m git status -On branch master - -No commits yet - -Untracked files: - (use "git add <file>..." to include in what will be committed) - ␛[31mbasnicka.txt␛[m - -nothing added to commit but untracked files present (use "git add" to track) -``` - -U každého nového souboru musíme Gitu říct, že -chceme jeho obsah sledovat. -Proveď to se svojí básničkou: - -```ansi -␛[36m$␛[0m git add basnicka.txt -``` - -a znovu zkontroluj stav repozitáře: - -```ansi -␛[36m$␛[0m git status -On branch master - -No commits yet - -Changes to be committed: - (use "git rm --cached <file>..." to unstage) - ␛[32mnew file: basnicka.txt␛[m - -``` - -To, co je zelené („changes to be committed“), -se přidá do další *revize* (angl. *commit*), -kterou vytvoříš. -Pojď tedy vytvořit revizi: - -```ansi -␛[36m$␛[0m git commit -[master (root-commit) eb0fcd9] První revize - 1 file changed, 6 insertions(+) - create mode 100644 basnicka.txt -``` - -Po zadání tohoto příkazu se otevře editor, -do kterého musíš napsat nějaký popisek, -abys věděl{{a}}, co tahle revize obsahuje za změny. -Pro začátek napiš jen `První revize`. -Předvyplněné řádky začínající `#` nech být -(nebo vymaž, podle chuti – Git je ignoruje). -Pak soubor ulož a zavři editor. - -> [note] Jak na editory? -> -> Na Windows, máš-li -> [správně nastavený Git]({{ lesson_url('git/install') }}), -> se použije Poznámkový blok (Notepad) – stačí něco -> napsat, uložit (<kbd>Ctrl</kbd>+<kbd>S</kbd>) a zavřít -> (<kbd>Alt</kbd>+<kbd>F4</kbd>). -> -> Na Linuxu a macOS se objeví editor v příkazové řádce, -> který se jmenuje Nano. -> Pozná se tak, že v dolních dvou řádcích má malou nápovědu. -> Něco napiš, pomocí <kbd>Ctrl</kbd>+<kbd>O</kbd> -> soubor ulož, potvrď jméno souboru (<kbd>Enter</kbd>) -> a pomocí <kbd>Ctrl</kbd>+<kbd>X</kbd> editor zavři. -> -> Nemáš-li Git nastavený podle instrukcí, objeví se přímo -> v příkazové řádce Vim – poměrně složitý editor, který -> se teď učit nebudeme. Pozná se tak, že úplně -> spodní řádek je prázdný. -> V takovém případě stiskni -> <kbd>Esc</kbd>, napiš `:q!` (dvojtečka, Q, vykřičník) -> a potvrď pomocí <kbd>Enter</kbd>. -> Pak si nastav Git a zkus `git commit` znovu. - - -Znovu zkus vypsat stav repozitáře: - -```ansi -␛[36m$␛[0m git status -On branch master -nothing to commit, working tree clean -``` - -Tenhle krátký výstup znamená, že od poslední revize -se nic nezměnilo. -Což dává smysl – poslední revizi jsi právě vytvořil{{a}}! - -A co všechno je v téhle první/poslední revizi? -To ti poví příkaz `git show`: - -```ansi -␛[36m$␛[0m git show -␛[33mcommit eb0fcd9317cbba3d9406ffe2918dfaad667f100f␛[m -Author: Adéla Novotná <adela.novotna@example.cz> -Date: Mon May 18 16:18:40 2020 +0200 - - První revize - -␛[1mdiff --git a/basnicka.txt b/basnicka.txt␛[m -␛[1mnew file mode 100644␛[m -␛[1mindex 0000000..558d133␛[m -␛[1m--- /dev/null␛[m -␛[1m+++ b/basnicka.txt␛[m -␛[36m@@ -0,0 +1,6 @@␛[m -␛[32m+␛[m␛[32mHolka modrooká, nesedávej u potoka␛[m -␛[32m+␛[m␛[32mHolka modrooká, nesedávej tam␛[m -␛[32m+␛[m -␛[32m+␛[m␛[32mV potoce je hastrmánek␛[m -␛[32m+␛[m␛[32mZatahá tě za copánek␛[m -␛[32m+␛[m␛[32mHolka modrooká, nesedávej tam␛[m -``` - -Vidíš unikátní -<span class="yellow">označení revize</span>, -pomocí kterého se vždy bude dát dostat k této konkrétní -verzi projektu. -Pak je tam jméno autor{{ gnd('a', 'ky', both='a') }} a datum vytvoření, -popisek -a nakonec shrnutí změn: byl přidán soubor <tt class="strong">basnicka.txt</tt> -s nějakým <span class="green">obsahem</span>. - -> [note] -> Když je výpis moc dlouhý, můžeš se v něm pohybovat -> (<kbd>↓</kbd>, <kbd>↑</kbd>, <kbd>PgUp</kbd>, <kbd>PgDn</kbd>) -> a zpět se dostaneš klávesou <kbd>Q</kbd> jako *Quit*. - -> [note] Kódování ve Windows -> Pokud výpis nezvládá znaky s diakritikou, zadej před `git show` příkaz -> -> ```dosvenv -> > set LC_ALL=C.UTF-8 -> ``` -> -> Tento příkaz nastaví aktuální terminál: když si otevřeš nové okno -> s příkazovou řádkou, bude ho potřeba zadat znovu. - - -## Druhá revize - -Udělej v básničce nějakou malou změnu – změň slovo, -uprav interpunkci nebo přidej sloku. -Pak se opět zeptej Gitu na stav repozitáře. - -```ansi -␛[36m$␛[0m git status -On branch master -Changes not staged for commit: - (use "git add <file>..." to update what will be committed) - (use "git restore <file>..." to discard changes in working directory) - ␛[31mmodified: basnicka.txt␛[m - -no changes added to commit (use "git add" and/or "git commit -a") -``` - -Soubor je opět červený! Něco se v něm změnilo! -Ale co? -Na to nám odpoví příkaz <code>git diff</code>. - -```ansi -␛[36m$␛[0m git diff -␛[1mdiff --git a/basnicka.txt b/basnicka.txt␛[m -␛[1mindex 558d133..24e2384 100644␛[m -␛[1m--- a/basnicka.txt␛[m -␛[1m+++ b/basnicka.txt␛[m -␛[36m@@ -1,6 +1,9 @@␛[m -␛[31m-Holka modrooká, nesedávej u potoka␛[m -␛[31m-Holka modrooká, nesedávej tam␛[m -␛[32m+␛[m␛[32mHolka modrooká␛[m -␛[32m+␛[m␛[32mNesedávej u potoka␛[m -␛[32m+␛[m␛[32mHolka modrooká␛[m -␛[32m+␛[m␛[32mNesedávej tam␛[m - ␛[m - V potoce je hastrmánek␛[m - Zatahá tě za copánek␛[m -␛[31m-Holka modrooká, nesedávej tam␛[m -␛[32m+␛[m␛[32mHolka modrooká␛[m -␛[32m+␛[m␛[32mNesedávej tam␛[m -``` - -Změny se ukazují po řádcích. -Červeně, s <tt class="red">-</tt>, jsou ukázány -odebrané řádky; zeleně s <tt class="green">+</tt> -řádky přidané. - -> [note] -> Změnilo-li se na řádku jen jedno slovo nebo i písmeno, -> celý řádek se ukáže jako smazaný a zase přidaný. -> Dá se to nastavit i jinak, když je potřeba, -> ale je dobré si na tento standard zvyknout. - -Takhle se dá jednoduše zjistit, co se dělo od poslední verze. -Když ti program přestane fungovat (a v poslední uložené -revizi fungoval), použij <code>git diff</code> – -v jedné ze změn musí být chyba! - -> [note] -> Řádek začínající <tt class="blue">@@</tt> říká, -> kde v souboru změna je (u mě začínal vypsaný kousek -> souboru řádkem 1 a měl 6 řádků; v nové verzi je -> opět od 1. řádku, ale narostl na 9). - -Jsi-li se změnami spokojen{{a}}, řekni Gitu, ať je -použije v další revizi: - -```ansi -␛[36m$␛[0m git add basnicka.txt -``` - -A pro úplnost se znovu koukni, co říká -`status` – co je zelené, přidá se do další -revize. - -```ansi -On branch master -Changes to be committed: - (use "git restore --staged <file>..." to unstage) - ␛[32mmodified: basnicka.txt␛[m - -``` - -Než uděláš druhou revizi, ještě řeknu něco o tom, -jak správně psát k revizím popisky. -Na to je totiž úzus, který téměř všichni programátoři -respektují: na prvním řádku je krátké shrnutí změn, -následuje prázdný řádek a pak detailnější popis důvodů -ke změně a případně změny samotné. -Snaž se délku řádků držet do zhruba 70 znaků; -vodítkem můžou být předvyplněné řádky začínající `#`. -Nemá cenu popisovat, co je jasné ze změn samotných, -zajímavé jsou hlavně širší souvislosti a důvody ke změnám. -Cokoli, co může přijít vhod, až se změny bude snažit někdo pochopit. -(Ten někdo můžeš být klidně ty, za pár měsíců.) - -Můj popisek bude znít takhle: - -```plain -Rozdělení dlouhých řádků - -Verše básně se většinou píšou na jednotlivé řádky. Myslím, že -takhle se to líp čte. (Ale, co si budeme povídat, hlavní -důvod je ukázat co dělá git diff.) -``` - -> [note] -> Nebude-li se ti někdy dařit shrnout změnu -> v 70 znacích, zamysli se, jestli neděláš moc velkou -> změnu najednou – např. "změna řetězce X -> a dopsání nového cyklu Y" by bylo lepší uložit -> jako dvě různé revize. - -Pomocí `git commit` vytvoř druhou revizi. -Pak ji zkontroluj: - -```ansi -␛[36m$␛[0m git show -␛[33mcommit 1fcd654a331f290616c948d9841fd8d2a34aa6b4␛[m -Author: Adéla Novotná <adela.novotna@example.cz> -Date: Mon May 18 16:18:40 2020 +0200 - - Rozdělení dlouhých řádků - - Verše básně se většinou píšou na jednotlivé řádky. Myslím, že - takhle se to líp čte. (Ale, co si budeme povídat, hlavní - důvod je ukázat co dělá git diff.) - -␛[1mdiff --git a/basnicka.txt b/basnicka.txt␛[m -␛[1mindex 558d133..24e2384 100644␛[m -␛[1m--- a/basnicka.txt␛[m -␛[1m+++ b/basnicka.txt␛[m -␛[36m@@ -1,6 +1,9 @@␛[m -␛[31m-Holka modrooká, nesedávej u potoka␛[m -␛[31m-Holka modrooká, nesedávej tam␛[m -␛[32m+␛[m␛[32mHolka modrooká␛[m -␛[32m+␛[m␛[32mNesedávej u potoka␛[m -␛[32m+␛[m␛[32mHolka modrooká␛[m -␛[32m+␛[m␛[32mNesedávej tam␛[m - ␛[m - V potoce je hastrmánek␛[m - Zatahá tě za copánek␛[m -␛[31m-Holka modrooká, nesedávej tam␛[m -␛[32m+␛[m␛[32mHolka modrooká␛[m -␛[32m+␛[m␛[32mNesedávej tam␛[m -``` - -## Diagram -Pro lepší pochopení, co dělají jednotlivé příkazy a v jakém -stavu můžou být soubory/změny, přikládám tento diagram: - -{{ figure( - img=static('diagram.svg'), - alt="Diagram revizí" -) }} - -## Log - -Teď, když máme za sebou první(ch) pár revizí, -si ukážeme několik příkazů, které nám umožní se -v nich orientovat. -První z nich je <code>git log</code>. - -```ansi -␛[36m$␛[0m git log -␛[33mcommit 1fcd654a331f290616c948d9841fd8d2a34aa6b4␛[m -Author: Adéla Novotná <adela.novotna@example.cz> -Date: Mon May 18 16:18:40 2020 +0200 - - Rozdělení dlouhých řádků - - Verše básně se většinou píšou na jednotlivé řádky. Myslím, že - takhle se to líp čte. (Ale, co si budeme povídat, hlavní - důvod je ukázat co dělá git diff.) - -␛[33mcommit eb0fcd9317cbba3d9406ffe2918dfaad667f100f␛[m -Author: Adéla Novotná <adela.novotna@example.cz> -Date: Mon May 18 16:18:40 2020 +0200 - - První revize -``` - -Git log vypíše všechny revize od té nejnovější až po -úplný začátek projektu. - -Až budeš mít verzí tolik, že se nevejdou najednou -na obrazovku, můžeš se v logu pohybovat pomocí šipek a -<kbd>PgUp</kbd>/<kbd>PgDn</kbd>. -„Ven“ se dostaneš klávesou <kbd>q</kbd>. - - -> [note] -> Je spousta možností jak vypisovat historii pomocí `git log`. -> Všechno je podrobně – možná až moc podrobně – -> popsáno v dokumentaci; stačí zadat `git help log`. -> „Ven“ z dokumentace se opět dostaneš klávesou <kbd>q</kbd>. -> -> Já často používám `git log --oneline --graph --decorate --cherry-mark --boundary`. -> Chceš-li tyhle možnosti studovat, začni v tomto -> pořadí a dej si pauzu vždycky, když přestaneš -> rozumět. :) - -Když se na nějakou verzi budeš chtít podívat podrobněji, -napiš `git show 5ff0b`, kde místo `5ff0b` -uveď prvních několik čísel z <span class="yellow">označení revize</span>. - -## gitk - -Z příkazové řádky se dá vyčíst všechno potřebné, -ale chce to trochu praxe. -Někdy je přehlednější použít grafické „klikátko“ jménem -*gitk*, které se dá spustit příkazem -`gitk --all`: - -```console -$ gitk --all -``` - -{{ figure( - img=static('gitk.png'), - alt="", -) }} - - -Tenhle program vypadá celkem šeredně (skoro jako by ho -psali programátoři, které místo designu zajímá, co je -„vevnitř“), ale pro naše účely postačí. -Zkus se v něm trochu zorientovat, pak ho zavři, -udělej dalších pár revizí a koukni se na ně přes -`git log` a `gitk --all`. - -## Závěr - -A to je všechno, co z Gitu zatím budeš potřebovat. -Vždycky, když uděláš <code>git add <var>soubor</var></code> -a `git commit`, -aktuální verze souborů se uloží a už nejde (jednoduše) -smazat – pokud nesmažeš celý adresář `.git`. -Jednotlivé verze a změny od posledního uložení, -si umíš i prohlížet. - -Možná to všechno zní jako zbytečně moc práce. -Máš tak trochu pravdu – naše projekty jsou zatím -dost malé na to, aby se jen pro ně vyplatilo učit Git. -Ale je dobré ho používat už od začátku. -Až bude správa verzí opravdu potřeba, bude se tenhle -trénink hodit. - -Takže odteď, kdykoliv uděláš v rámci PyLadies funkční -verzi nějakého programu, pomocí `git add` a `git commit` si ji ulož do Gitu. diff --git a/lessons/git/basics/info.yml b/lessons/git/basics/info.yml deleted file mode 100644 index 9c5ec3b3..00000000 --- a/lessons/git/basics/info.yml +++ /dev/null @@ -1,10 +0,0 @@ -title: Git -style: md -css: | - .green { color: #0a0; } - .red { color: #a00; } - .yellow { color: #a50; } - .strong { font-weight: bold; } - .blue { color: #0aa; } -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2015-2017. -license: cc-by-sa-40 diff --git a/lessons/git/basics_branching.sh b/lessons/git/basics_branching.sh deleted file mode 100644 index 7cc7722e..00000000 --- a/lessons/git/basics_branching.sh +++ /dev/null @@ -1,131 +0,0 @@ -git init - -git status - -cat > basnicka.txt << END -Holka modrooká, nesedávej u potoka -Holka modrooká, nesedávej tam - -V potoce je hastrmánek -Zatahá tě za copánek -Holka modrooká, nesedávej tam -END - -git status -git add basnicka.txt -git status -GIT_EDITOR='echo "První revize" >' git commit - -git status -git show - -cat > basnicka.txt << END -Holka modrooká -Nesedávej u potoka -Holka modrooká -Nesedávej tam - -V potoce je hastrmánek -Zatahá tě za copánek -Holka modrooká -Nesedávej tam -END - -git status -git diff -git add basnicka.txt -git status - -GIT_EDITOR="echo \"$second_msg\" >" git commit - -git show -git log - -git config -l - -take_screenshot $OUTFILE.gitk.png gitk --all - -git add basnicka.txt -GIT_EDITOR="echo \"$second_msg\" >" git commit - -git branch -git branch doplneni-autora -git branch -git checkout doplneni-autora -git branch - -cat > basnicka.txt << END -(Lidová) - -Holka modrooká -Nesedávej u potoka -Holka modrooká -Nesedávej tam - -V potoce je hastrmánek -Zatahá tě za copánek -Holka modrooká -Nesedávej tam -END - -git add basnicka.txt -GIT_EDITOR='echo "Doplnění autora" >' git commit - -take_screenshot $OUTFILE.branch1.png gitk --all - -git checkout master -git branch doplneni-jmena -git checkout doplneni-jmena -git branch - -cat > basnicka.txt << END -Holka Modrooká - -Holka modrooká -Nesedávej u potoka -Holka modrooká -Nesedávej tam - -V potoce je hastrmánek -Zatahá tě za copánek -Holka modrooká -Nesedávej tam -END - -git add basnicka.txt -GIT_EDITOR='echo "Doplnění jména" >' git commit - -take_screenshot $OUTFILE.branches.png gitk --all - -git checkout master -git merge doplneni-jmena -git merge doplneni-autora -git status -cat basnicka.txt -git diff - -cat > basnicka.txt << END -Holka modrooká -(Lidová) - -Holka modrooká -Nesedávej u potoka -Holka modrooká -Nesedávej tam - -V potoce je hastrmánek -Zatahá tě za copánek -Holka modrooká -Nesedávej tam -END - -git add basnicka.txt -GIT_EDITOR='true' git commit - -git branch - -take_screenshot $OUTFILE.merge.png gitk --all - -git branch -d doplneni-autora -git branch -d doplneni-jmena -git branch diff --git a/lessons/git/branching/index.md b/lessons/git/branching/index.md deleted file mode 100644 index 5b1cfb61..00000000 --- a/lessons/git/branching/index.md +++ /dev/null @@ -1,251 +0,0 @@ -# Větvení v Gitu - -Takže, Git už znáš! -Teď to začne být trošičku složitější :) - -Programáto{{ gnd('ři', 'rky', both='ři') }} občas potřebují pracovat na dvou -věcech zároveň. -V projektu do práce se objeví se chyba, -která musí být opravená -ještě dnes, tak programátor{{ gnd('', 'ka') }} opustí, co zrovna dělá, -vrátí se k nějaké „stabilní” verzi, opraví chybu -a odešle ji zákazníkům. -A pak se vrátí k tomu, co dělal{{a}} předtím – jen ještě -musí zakomponovat opravu chyby i do verze, na které -pracuje dlouhodobě. - -Git na to má takzvané *větve* (angl. *branches*). -Na jedné „větvi” se pracuje, ale je možné se přepnout do -jiné (třeba starší) větve, udělat pár změn -a pak se zase přepnout do nové větve a -pokračovat dál nebo sloučit změny. - -Větvení využijeme i při spolupráci více lidí – každý -dělá na vlastní větvi a když přijde čas, -tak se různé změny sloučí dohromady. - -Podívej se, jaké máš větve ve svém repozitáři. -K tomu slouží příkaz `git branch`: - -```ansi -␛[36m$␛[0m git branch -* ␛[32mmaster␛[m -``` - -Je tam jenom jedna a jmenuje se `master` -– to je tradičně jméno „hlavní” větve. - -K vytvoření nové větve znovu použiješ -`git branch`, jen tomu příkazu dáš navíc -jméno nové větve. -Třeba budeš chtít k básničce doplnit jméno autora, -tak větev pojmenuješ `doplneni-autora`. - -```ansi -␛[36m$␛[0m git branch doplneni-autora -␛[36m$␛[0m git branch - doplneni-autora␛[m -* ␛[32mmaster␛[m -``` - -Tenhle příkaz sice udělal novou větev, -ale nepřepnul do ní. -Hvězdička ve výstupu z `git branch` ukazuje, -že stále pracuješ v `master`. -Na přepnutí budeš potřebovat další příkaz: - -```ansi -␛[36m$␛[0m git checkout doplneni-autora -Switched to branch 'doplneni-autora' -␛[36m$␛[0m git branch -* ␛[32mdoplneni-autora␛[m - master␛[m -``` - -Tak. Teď jsi „ve” větvi `doplneni-autora`. -Doplň nějaké jméno na začátek souboru `basnicka.txt`, -a pomocí `git add` a `git commit` udělej novou revizi. -Pak koukni na `gitk --all`, jak to vypadá: - -{{ figure( - img=static('branch1.png'), - alt="Výstup programu `gitk` s větví doplneni-autora", -) }} - -Aktuální větev – `doplneni-autora` – je -zvýrazněná tučně a starší `master` je stále -na původní revizi. - -Opusťme teď na chvíli práci na doplňování autora. -Vrať se do větve `master` a vytvoř z ní -větev `doplneni-jmena`. -Pak se na tuhle novou větev přepni. - -```ansi -␛[36m$␛[0m git checkout master -Switched to branch 'master' -␛[36m$␛[0m git branch doplneni-jmena -␛[36m$␛[0m git checkout doplneni-jmena -Switched to branch 'doplneni-jmena' -␛[36m$␛[0m git branch - doplneni-autora␛[m -* ␛[32mdoplneni-jmena␛[m - master␛[m -``` - -Doplň jméno básně na začátek souboru (tedy na stejné místo, -jako je v druhé větvi název) a pomocí -`git add`, `git commit` ulož revizi. -Všechno zkontroluj přes `gitk --all`. - -{{ figure( - img=static('branches.png'), - alt="Výstup programu `gitk` s větvemi doplneni-autora a doplneni-nazvu", -) }} - - -Takhle nějak se dá postupovat v situaci popsané v úvodu: -opuštění rozpracované verze, přechod na „stabilní” -verzi `master` a začátek práce v jiné -části projektu. - -Mezi jednotlivými větvemi se dá podle libosti přepínat, -jen je vždycky dobré před přepnutím udělat novou revizi -(`git commit`) a pomocí `git status` zkontrolovat, jestli je všechno -uložené v Gitu. - -Na stejném principu funguje i spolupráce několika lidí -na jednom projektu: je nějaký společný základ -(`master`) a každý dělá na vlastní větvi, dokud není se svými změnami spokojený. - -A až je některá větev hotová, může se začlenit -zpátky do `master`. Podívejme se jak na to. - - -## Sloučení - -Nedávalo by smysl historii projektu rozdvojovat, -kdyby pak jednotlivé větve nešly zase sloučit dohromady. -V Gitu je většinou slučování poměrně jednoduché, ale tento příklad schválně -ukazuje nejsložitější variantu, která může nastat. - -Přepni se zpátky na `master` -a použij příkaz `git merge`, který -sloučí jinou větev s tou aktuální. -Příkazu musíš dát jméno větve, kterou chceš sloučit. - -```ansi -␛[36m$␛[0m git checkout master -Switched to branch 'master' -␛[36m$␛[0m git merge doplneni-jmena -Updating 1fcd654..5c9bf93 -Fast-forward - basnicka.txt | 3 ␛[32m+++␛[m - 1 file changed, 3 insertions(+) -``` - -Sloučeno! Ono „`Fast-forward`” znamená, že -vlastně nebylo co slučovat – jen se do větve -`master` přidaly nové změny. -Zkontroluj v `gitk --all`, jak to vypadá. - -A pak zkus sloučit i druhou větev: `git merge doplneni-autora`. -Tady to bude složitější: pravděpodobně se stane, že změny nepůjdou -automaticky sloučit a ve výstupu se objeví hláška -`merge conflict` (slučovací konflikt): - -```ansi -␛[36m$␛[0m git merge doplneni-autora -Auto-merging basnicka.txt -CONFLICT (content): Merge conflict in basnicka.txt -Automatic merge failed; fix conflicts and then commit the result. -``` - -> [note] A když ne? -> Jestli se konflikt neobjevil, Git změny sloučil sám. -> Gratuluji! Zbytek téhle sekce bude jen teoretický; vrať se sem, až se ti -> někdy „podaří“ konflikt udělat. - -Když nastane konflikt, `git status` ukáže „both modified“ – tedy že byl soubor -změněný v obou slučovaných větvích: - -```ansi -␛[36m$␛[0m git status -On branch master -You have unmerged paths. - (fix conflicts and run "git commit") - (use "git merge --abort" to abort the merge) - -Unmerged paths: - (use "git add <file>..." to mark resolution) - ␛[31mboth modified: basnicka.txt␛[m - -no changes added to commit (use "git add" and/or "git commit -a") -``` -V tom případě se na soubor podívej v editoru: objeví -se v něm obsah z obou konfliktních verzí, -společně se značkami `<<<<<<<`, `=======` a `>>>>>>>`, které upozorňují na -místo kde konflikt nastal. - -Značky ukáže i příkaz `git diff`, jehož výstup je teď trošku složitější: - -```ansi -␛[36m$␛[0m git diff -␛[1mdiff --cc basnicka.txt␛[m -␛[1mindex bc7a2de,88e7ea5..0000000␛[m -␛[1m--- a/basnicka.txt␛[m -␛[1m+++ b/basnicka.txt␛[m -␛[36m@@@ -1,4 -1,4 +1,8 @@@␛[m -␛[32m++<<<<<<< HEAD␛[m -␛[32m +Holka Modrooká␛[m -␛[32m++=======␛[m -␛[32m+ (Lidová)␛[m -␛[32m++>>>>>>> doplneni-autora␛[m - ␛[m - Holka modrooká␛[m - Nesedávej u potoka␛[m -``` - -Konflikty a značky, které můžou být nepřehledné, ukazuje Git ze stejného důvodu -jako Python chybové hlášky: tedy aby ti co nejvíce pomohl. -Git je jen „hloupý“ nástroj a s konfliktem si neporadí sám, ale snaží se -ti řešení konfliktu co nejvíc usnadnit. - -Proto hlavně nepanikař! -Soubor otevři v editoru a uprav **tak, jak by měl vypadat**. -Značky jako `<<<<<<<` smaž; řádky poskládej tak, jak by měly jít za sebou. -(A pracuješ-li na kódu, spusť testy a ověř, jestli všechno stále funguje.) - -Pak soubor ulož a zadej `git commit`. - -Ať nastal konflikt nebo ne, vytvoří se „slučovací revize“ -(angl. *merge commit*), které – jako každé revizi – můžeš dát popisek. -Tentokrát je popisek už předvyplněný; chceš-li nějaký jiný, nahraď ho. - -```ansi -␛[36m$␛[0m git add basnicka.txt -␛[36m$␛[0m git commit -[master 884b30a] Merge branch 'doplneni-autora' -``` - -Povedlo se? - -{{ figure( - img=static('merge.png'), - alt="Výstup programu `gitk` s větvemi doplneni-autora a doplneni-nazvu sloučenými do master", -) }} - -Pokud ano, můžeš staré větve vymazat – všechny jejich -změny jsou v `master` a nemá na nich cenu -pracovat dál. - -```ansi -␛[36m$␛[0m git branch --delete doplneni-autora -Deleted branch doplneni-autora (was f1cd9be). -␛[36m$␛[0m git branch --delete doplneni-jmena -Deleted branch doplneni-jmena (was 5c9bf93). -␛[36m$␛[0m git branch -* ␛[32mmaster␛[m -``` - -Gratuluji, už umíš větvení a slučování! diff --git a/lessons/git/branching/info.yml b/lessons/git/branching/info.yml deleted file mode 100644 index 64cc3263..00000000 --- a/lessons/git/branching/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Větvení v Gitu -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2015-2017. -license: cc-by-sa-40 diff --git a/lessons/git/collaboration/index.md b/lessons/git/collaboration/index.md deleted file mode 100644 index 2ea7a47a..00000000 --- a/lessons/git/collaboration/index.md +++ /dev/null @@ -1,468 +0,0 @@ -{% set coach_username = var('coach-username') or 'naucse' %} - -# Spolupráce - -„Opravdové” programy zřídka vznikají prací jednoho člověka. -Víc hlav víc ví, a tak je dobré si na projekt vytvořit tým. - -Každý člen týmu potřebuje mít přístup k práci ostatních. -K tomu se dá použít Git: někde na internetu si zařídí *sdílený repozitář*, -se kterým se všichni budou synchronizovat. - -> [note] Pro samostudium -> Pokud materiály čteš z domu a máš možnost se -> v budoucnu dostat na nějaký sraz, zatím tuhle sekci přeskoč. -> Na sraze pak popros zkušenější programátory, aby ti pomohli. -> (Nechystáš-li se na sraz, můžeš pokračovat – -> zvládnout se to dá.) - -> [note] Pro kouče -> Udělej na GitHubu repozitář jménem `prezencka` a dej do -> něj soubor se svým jménem. Příklad je na -> [naucse/prezencka](https://github.com/naucse/prezencka). -> Nasdílej s účastníky příkaz na jeho naklonování (přes https). - - -## Open Source - -Nejde mluvit o Gitu a spolupráci a nezastavit se chvíli u otevřeného -zdrojového kódu. -První programy vznikaly v akademické sféře, kde byly zcela přirozeně sdíleny, -jako je to s poznatky mezi vědci běžné. -50\. a 60. léta byla obdobím velké kreativity, kdy vzniklo mnoho z konceptů -a technologií, které dnes používáme. -Pak se začalo programování postupně komercializovat a firmy začaly zdrojový -kód skrývat jako konkurenční výhodu. -Do té doby víceméně jednolitá komunita programátorů byla nucena se rozdělit. - -Některým programátorům tohle skrývání kódu hluboce vadilo. -Roku 1985 publikoval -[Richard Stallman](https://en.wikipedia.org/wiki/Richard_Stallman) -[GNU Manifesto](https://www.gnu.org/gnu/manifesto.en.html), -kde vysvětlil, proč hodlá vytvořit operační systém s otevřeným kódem a -odstartoval tak hnutí svobodného softwaru. -To prosazuje 4 následující svobody (převzato -z [Wikipedie](https://cs.wikipedia.org/wiki/Svobodn%C3%BD_software)): - -<ol start="0"> -<li>svoboda používat program za jakýmkoliv účelem,</li> -<li>svoboda studovat, jak program pracuje a možnost přizpůsobit ho svým potřebám,</li> -<li>svoboda redistribuovat kopie programu,</li> -<li>svoboda vylepšovat program a zveřejňovat zlepšení, aby z nich mohla mít prospěch celá komunita.</li> -</ol> - -Dnes je spousta projektů s otevřeným zdrojovým kódem (tzv. *open-source* projektů) -dostupná na Internetu a každý je používáme. -Jejich další sdílení je upraveno jednou z licencí, -které tyto základní svobody zaručují. - -Ne všechny jsou v Pythonu (a těm co jsou zatím -nebudeš všem rozumět). Ne všechny jsou v Gitu. -Ne všechny jsou kvalitní – protože si -každý může zveřejnit co chce, na Internetu se válí -spousta nedodělků, opuštěných nápadů a nepodařených experimentů. -A bohužel, ne všechny projekty mají přátelské autory. - -Na druhou stranu ale open-source programy -mají svoje výhody: nejenom že se z nich může kdokoli -učit, ale každý může i zkontrolovat, jestli -dělají to, co dělat mají. -Populární open-source programy tě například -pravděpodobně nebudou špehovat (tj. hlásit autorovi, -co na počítači děláš), ani většinou neobsahují -reklamy: kdyby to dělaly, najde se -někdo kdo tyhle „funkce” odstraní a lidi – časem – -začnou používat opravenou verzi. - -Některé příklady populárních open-source projektů: - -* [Mozilla Firefox](https://github.com/mozilla/gecko-dev), - [Chromium](https://chromium.googlesource.com/chromium/src.git) - (prohlížeče) -* [VS Code - OSS](https://github.com/Microsoft/vscode), - [Atom](https://github.com/atom/atom), - [gedit](https://github.com/GNOME/gedit) - (textové editory) -* [CPython](https://github.com/python/cpython) - (jazyk Python) -* [Linux](https://github.com/torvalds/linux), - [Android](https://github.com/aosp-mirror) - (jádra operačních systémů) -* [Pytest](https://github.com/pytest-dev/pytest/) - (pythonní knihovna na testování) -* [Django](https://github.com/django/django), - [Flask](https://github.com/mitsuhiko/flask), - [Requests](https://github.com/kennethreitz/requests) - (webové knihovny pro Python) -* [NumPy](https://github.com/numpy/numpy), - [Jupyter](https://github.com/jupyter/notebook), - [Matplotlib](https://github.com/matplotlib/matplotlib) - (pythonní knihovny pro vědce a analytiky) -* [Materiály](https://github.com/pyvec/naucse.python.cz) k tomuto kurzu - -Jak vidno z posledního příkladu, nejen softwarové -projekty se dají vést takhle veřejně. -Tento kurz vychází z principů open source: -všechno know-how je sdílené a budeme rádi, když -se zapojíš. - -Až příště uvidíš v materiálech chybu (nebo jestli o nějaké víž už teď), -dnes se dozvíš, jak ji opravit! - -A co tvůj kód? Chceš ho taky dávat takhle veřejně k dispozici? -Nutné to samozřejmě není – Git se dá používat i -v uzavřeném týmu – ale na druhou stranu, -máš důvod proč to nedělat? -Zveřejňovat zdrojový kód se hodí už jen pro to, -aby ti s ním mohli zkušenější programátoři snadněji pomáhat. - - -## GitHub - -Na Internetu existuje spousta stránek, kam se dají nahrávat gitové repozitáře -s kódem – např. [GitLab](https://gitlab.com/), -[BitBucket](https://bitbucket.org), -[Pagure](https://pagure.io/) nebo -[Launchpad](https://launchpad.net/). -Aktuálně nejpopulárnější je ale [GitHub](https://github.com), který si tady -ukážeme. - -Jestli ještě nemáš uživatelský účet na [github.com](https://github.com), jdi -tam a založ si ho. - - -## Naklonování repozitáře <small>(<code>git clone</code>)</small> - -Pro začátek zkusíme práci s repozitářem, který už vytvořil někdo jiný. -V příkazové řádce zadej příkaz, který ti oznámí kouč; něco jako - -```console -$ git clone https://github.com/{{coach_username}}/prezencka -``` - -Vytvoří se ti nový repozitář – adresář se jménem -`prezencka`, ve kterém je nějaký soubor. - - -Na URL (adresu), kterou jsi v tomhle příkladě -použil{{a}}, se můžeš podívat i v prohlížeči. -Uvidíš seznam souborů a spoustu odkazů k -informacím o repozitáři (například pod „commits” -je historie). - -Přepni se do nového adresáře (`cd prezencka`) -a zkus se podívat na historii (`gitk` nebo `git log`). -Možná je krátká, ale hlavně, že nějaká je. -Máš na počítači kopii projektu, který založil někdo jiný! - -Jak už napovídá název repozitáře, tvůj příspěvek do tohoto projektu bude -zápis do prezenčky: konkrétně přidání souboru s tvým jménem. -Jméno je to proto, aby nedocházelo ke kolizím: potřebujeme, aby příspěvky od -všech lidí, kteří prochází tenhle kurz, byly jiné. - -Tvůj příspěvek bude ovšem veřejně vystaven na internetu. -Pokud nechceš vystavovat svoje občanské jméno, použij místo něj klidně -přezdívku, oblíbené jídlo nebo pár náhodných písmen. Ale: -* když budeš pojmenovávat soubor, buď originální, aby nedošlo ke konfliktům, a -* nesdílej nic, co nemáš právo sdílet (např. texty moderních písní). - - -## Vytvoření větve - -Pomocí `git branch` zjisti, na jaké jsi aktuálně větvi. -Měla by to být větev `master`. - -Tuhle „základní“ větev je dobré používat jen na revize, na kterých se už -shodl celý tým. -Proto když chceš do projektu přispět, jako první krok si pro svůj příspěvek -udělej novou větev a přepni se do ní. -Například pomocí: - -```console -$ git branch pridani-jmena -$ git checkout pridani-jmena -``` - - -## Posílání změn <small>(<code>git push</code>)</small> - -Teď se do projektu zapoj. -Přidej soubor pojmenovaný podle tvého jména (nebo přezdívky) -a dej ho do gitu (<code>git add <var>...</var></code>; <code>git commit</code>). - -Teď zbývá „jen” změnu začlenit do původního sdíleného repozitáře. -To ale není jen tak: repozitář, který jsi -naklonoval{{a}}, patří koučovi. A tomu by se asi -nelíbilo, kdyby kdokoliv na Internetu mohl přijít -a nahrát mu do repozitáře změny. - -Spousta míst na Internetu (blogy, zpravodajství, e-shopy) funguje tak, že -vybraná skupina lidí, „editorů“, má právo měnit obsah, jak se jim líbí. -Takovým editorům musí správce projektu věřit, než jim přístup povolí. - -S Gitem se používá trošku jiný mechanismus: -změny nahraješ do *vlastního* sdíleného -repozitáře, který máš právo měnit jen ty. -Majiteli původního projektu pak napíšeš -žádost o začlenění těch změn (angl. *pull request*). -Může to být třeba mail se slovy „Hele, na té a té -adrese mám nějaké změny, které by se ti mohly hodit! -Přidej je do svého projektu!” - -Výhoda je v tom, že se do projektu – pokud je -veřejný – může zapojit kdokoliv. Nemusíš se -předem ptát, nemusíš dokazovat že jsi důvěryhodná -osoba, stačí něco změnit a poslat. -Jestli se změna bude autorům projektu líbit nebo -ne, to už je jiná věc – ale můžou posuzovat samotnou -změnu, ne důvěryhodnost jejího autora. - -Služby jako [github.com](https://github.com/) -ti umožňují si udělat vlastní sdílený repozitář (který bude k dispozici na -internetu) a zjednodušují začleňování změn (místo posílání mailů stačí -zmáčknout tlačítko). Pojďme se podívat, jak na to. - -Přihlaš se na GitHub a pak zajdi na adresu -kterou jsi použil{{a}} pro `git clone`. -Vlevo nahoře najdi tlačítko „Fork” a klikni na něj. -Tím si vytvoříš na GitHubu vlastní kopii repozitáře: -adresa by měla být něco jako -<code>https://github.com/<i>tvojejmeno</i>/prezencka</code>. - - -> [note] -> Kdybys měl{{a}} v různých kopiích repozitáře zmatek, -> přijde vhod malé vysvětlení: jedna kopie je původní -> projekt na GitHubu, kam správce projektu dává -> aktuální „oficiální“ nebo „hlavní“ verzi. Další kopie na GitHubu -> je „tvoje“ a můžeš si do ní nahrát co chceš -> (nejčastěji v ní ale zveřejňuješ změny, které můžou -> být užitečné pro ostatní). -> Tyhle dvě kopie existují na serverech GitHubu a jsou volně dostupné -> přes internet. -> -> Třetí kopii repozitáře pak máš u sebe na počítači. -> K té se dostaneš jen ty. -> -> Z „hlavní“ verze si stáhneš práci ostatních členů týmu; -> do *tvého* projektu na GitHubu dáváš své změny, aby je ostatní mohli -> schválit a začlenit do „hlavní“ verze. -> -> {{ figure( - img=static('gh-workflow-diagram.svg'), - alt='Diagram tří repozitářů' -) }} - -A teď, jak z tvého počítače nahrát změny na GitHub? -Git si u každého repozitáře na tvém počítači -pamatuje adresy, odkud se dají stahovat -a kam se dají posílat změny. -Seznam těchhle adres ti ukáže příkaz `git remote -v`. -Třeba: - -```console -$ git remote -v -origin https://github.com/{{coach_username}}/prezencka (fetch) -origin https://github.com/{{coach_username}}/prezencka (push) -``` - -Tenhle výstup znamená, že pod zkratkou „origin” -se schovává adresa, ze které jsi repozitář -naklonoval{{a}}. - -Přidej si podobnou zkratku pro vlastní repozitář na GitHubu. -Nezapomeň nahradit <i>tvojejmeno</i> za jméno účtu, -který máš na GitHubu ty. (Pozor, v příkazu je <i>tvojejmeno</i> dvakrát!) - -<div class="highlight codehilite"> -<pre><code><span class="gp">$</span> git remote add <i>tvojejmeno</i> https://github.com/<i>tvojejmeno</i>/prezencka -</code></pre></div> - -a zkontroluj si, že se to povedlo: - -<div class="highlight codehilite"> -<pre><code><span class="gp">$</span> git remote -v -<span class="go">origin git@github.com:{{coach_username}}/prezencka.git (fetch)</span> -<span class="go">origin git@github.com:{{coach_username}}/prezencka.git (push)</span> -<span class="go"><i>tvojejmeno</i> https://github.com/<i>tvojejmeno</i>/prezencka (fetch)</span> -<span class="go"><i>tvojejmeno</i> https://github.com/<i>tvojejmeno</i>/prezencka (push)</span> -</code></pre></div> - -Tolik k nastavení – `git remote add` -stačí udělat jednou pro každý repozitář. -Pak už můžeš změny nahrávat pomocí: - - -<div class="highlight codehilite"> -<pre><code><span class="gp">$</span> git push <i>tvojejmeno</i> pridani-jmena -</code></pre></div> - -což znamená: pošli na adresu uloženou pod zkratkou -<code><i>tvojejmeno</i></code> -větev `pridani-jmena`. - -Funguje? Podívej se na -<code>https://github.com/<i>tvojejmeno</i>/prezencka</code> -v prohlížeči a ujisti se, že tam tvoje změny jsou. - - -## Žádost o začlenění <small>(<em>pull request</em>)</small> - -Teď zbývá požádat autory původního projektu, -aby změny z tvého sdíleného repozitáře přidali do svojí kopie. -GitHub na to má mechanismus zvaný *pull request* (žádost o začlenění). - -Jdi na stránku původního projektu (na adresu, -kterou jsi použil{{a}} na začátku pro -`git clone`). -Měl{{a}} bys tam vidět oznámení o své nově nahrané větvi -s velkým zeleným tlačítkem *Compare & pull request*. -Klikni na něj. Pokud chceš, tak dopiš/změň popisek -toho, co tahle změna obnáší. -Pak zmáčkni další tlačítko. - -> [note] -> Jestli tlačítko *Compare & pull request* nevidíš, běž na adresu -> *své* kopie repozitáře a stiskni tlačítko *New pull request*. -> Vyber, co kam chceš začlenit, dopiš/změň popisek a pak zmáčkni -> *Create pull request*. - -Hotovo; teď je na autorech projektu, aby -se na změny podívali a přijali – nebo začali diskusi -o tom, jak je ještě vylepšit. -(Diskutovat se dá na stránce *pull requestu* nebo přes mail.) - -> [note] Pro samostudium -> Procházíš-li materiály z domu, musíš teď počkat, -> než si někdo tvé žádosti všimne a začlení ji. -> To může trvat i pár dní; kdyby to bylo přes týden, -> tak se na stránce *pull requestu* zkus připomenout. - -U přidání jména do prezenčky se to asi nestane, ale kdybys potřeboval{{a}} -na změně před začleněním ještě trochu zapracovat (třeba i po -pár dnech diskuse), nebyl by to problém. -Přepni se na svém počítači do větve `pridani-jmena`, udělej další revize, -a pomocí <code>git push <i>tvojejmeno</i> pridani-jmena</code> -*pull request* aktualizuj. - - -## Aktualizace <small>(<code>git pull</code>)</small> - -Když budou tvé změny – a změny od ostatních – -začleněné, můžeš si aktualizovat lokální repozitář. (To je ten, -který máš u sebe na počítači.) - -Nejdřív se přepni zpět do větve `master`. -Teď už nebudeš pracovat na `pridani-jmena`; tahle větev už je odeslaná. - -To se dělá příkazem -`git pull origin master` (stáhni změny -z větve „master” z adresy pod zkratkou „origin”). -Pomocí `gitk --all` nebo `git log` -se můžeš podívat, jak se projekt mezitím vyvinul. - -Tohle `git pull` je dobré provést vždycky předtím, než začneš pracovat na -nové změně/větvi. -Zaručíš tím, že projekt, který měníš, je „čerstvý“. - -Gratuluji! Právě jsi {{gnd('prošel', 'prošla')}} „kolečkem“, -které většina programátorů dělá denně: udělání nějaké změny, -odeslání kolegům na kontrolu a začlenění a stažení změn od ostatních. - - -## Hlášení chyb <small>(<em>issues</em>)</small> - -Občas nastane situace, kdy v nějakém projektu -na GitHubu najdeš chybu, ale nemáš čas nebo -znalosti, abys ji opravil{{a}}. V takovém případě -často na GitHubu na stránce projektu pod záložkou *Issues* -najdeš seznam nahlášených problémů. -Nenajdeš-li mezi nimi „svoji” chybu, můžeš ji -nahlásit – stačí kliknout na *New Issue* -a můžeš psát, kdy chyba nastává, co program dělá -špatně a co by měl dělat místo toho. - -> [note] -> Některé projekty nepoužívají Issues na GitHubu. -> Kdybys záložku Issues {{gnd('nenašel', 'nenašla')}}, podívej se -> do dokumentace projektu, jestli tam není odkaz na -> seznam chyb. - -## README: Informace pro ostatní - -Pokud vytváříš projekt a chceš, aby do něj přispívali i ostatní, -je potřeba aby věděli, co tvůj projekt dělá, k čemu se hodí, -jak se používá a podobně. - -Na základní informace o projektu/repozitáři se používá soubor `README` -(z angl. _read me_, _čti mě_). -Do tohoto souboru patří mj.: - -* název projektu, -* stručný popis projektu (jedna až dvě věty), -* krátký návod k instalaci projektu, -* krátký návod ke spuštění projektu, -* krátký návod k používání projektu, případně odkaz na rozsáhlejší dokumentaci, -* pokud má projekt testy, informace o tom, jak je spustit, -* informace o tom, jak se zapojit do vývoje projektu, -* informace o autorech projektu, -* informace o licenci (více se licencích dozvíš později). - -README by mělo být členěné a jeho přečtení by nemělo zabrat uživateli hodinu, -většinou stačí krátké úderné informace s případným odkazem někam dál. -Nemusíš tedy například vysvětlovat v každém projektu, jak se instaluje Python. -Stačí říct, že Python je potřeba (a v jaké verzi) -a odkázat uživatele na patřičný návod. -Je také třeba brát v úvahu, kdo bude README číst. -Píšeš-li program pro jiné vývojářky a vývojáře, -často nemusíš zabrušovat do detailů. - -GitHub (a spousty jiných podobných služeb) umožňuje pro README použít nějaký -značkovací jazyk, například [Markdown](https://cs.wikipedia.org/wiki/Markdown). -Je možné pak používat nadpisy, obrázky apod. - -A v neposlední řadě: aby se do projektu mohl zapojit -kdokoli z celého světa, bývají open-source projekty v angličtině. -Jména proměnných, komentáře, dokumentace – všechno -je primárně v anglické verzi. -Tenhle kurz je česky, aby byly začátky jednodušší, -ale jestli se ti programování zalíbilo a chceš -v něm po kurzu pokračovat dál, bez angličtiny -to bude velice složité. - -## Licence - -Aby sdílení fungovalo i pro právní stránce, -nestačí když nahraješ kus kódu na Internet. -Musíš taky oficiálně oznámit, že si s ním ostatní můžou hrát. -Na svůj kód totiž máš autorské právo, podle kterého ostatní nesmí tvůj program -používat, natož vylepšovat, dokud jim to nepovolíš. -Pro formální udělení tohohle povolení se používají *licence*, které píšou -právníci. - -Problematika licencí může být, bohužel, docela složitá. -Když to ale zjednodušíme na minimum, budeš -chtít jen zajistit, aby každý mohl tvůj výtvor -používat, učit se z něj, předávat ho dál -a vylepšovat ho. V tom případě vyber třeba -licenci [MIT](https://choosealicense.com/licenses/mit/). - -> [note] -> Pokud chceš navíc zabránit tomu, že si tvůj kód -> někdo vezme a začne ho „vylepšovat“ a vydělávat na -> něm, aniž by se o vylepšení podělil s ostatními, -> zkus licenci [AGPL](https://choosealicense.com/licenses/agpl-3.0/). - -> [note] -> A tyto materiály jsou pod ještě jinou licencí – -> [CC BY-SA](https://choosealicense.com/licenses/cc-by-sa-4.0/) – -> protože výše jmenované licence jsou dělané na programy, ne na text. - -Kód se nejčastěji licencuje tak, že text licence -dáš do souboru jménem `LICENSE` a přidáš do Gitu. -Je dobré licenci zmínit i v souboru `README`. - -Chceš-li si o licencích přečíst něco víc, odkážu tě na -[choosealicense.com](http://choosealicense.com/), -případně [creativecommons.org](http://creativecommons.org/choose/) -a [opensource.org](https://opensource.org/licenses). diff --git a/lessons/git/collaboration/info.yml b/lessons/git/collaboration/info.yml deleted file mode 100644 index d6eb47cb..00000000 --- a/lessons/git/collaboration/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Spolupráce a Open source -style: md -attribution: Pro PyLadies CZ napsali Petr Viktorin a Oskar Hollman, 2015-2017. -license: cc-by-sa-40 diff --git a/lessons/git/create_examples.sh b/lessons/git/create_examples.sh deleted file mode 100644 index c6e59551..00000000 --- a/lessons/git/create_examples.sh +++ /dev/null @@ -1,55 +0,0 @@ -# Runs a Git demo script and outputs a file with terminal escape sequences -# where \e is substituted for the visible character `␛`. - -if [ -z "$2" ]; then - echo "Usage: $0 SCRIPT OUTFILE" - exit 1 -fi - -THE_SCRIPT=$(realpath $1) -OUTFILE=$(realpath $2) - -export second_msg=' -Rozdělení dlouhých řádků - -Verše básně se většinou píšou na jednotlivé řádky. Myslím, že -takhle se to líp čte. (Ale, co si budeme povídat, hlavní -důvod je ukázat co dělá git diff.) -' - -function take_screenshot { - screenshot_name=$1 - shift - $* & - pid=$! - sleep 0.5 - /usr/bin/import -window root $screenshot_name - kill $pid - wait -} - -_tempdir=$(mktemp -d) -_confdir=$(mktemp -d) - -export HOME=$_confdir -cat > $_confdir/.gitconfig << END -[user] - name = Adéla Novotná - email = adela.novotna@example.cz -[color] - ui = always -END - -cd $_confdir -python3 -m venv env -. env/bin/activate - -cd $_tempdir -PS4='————————————————————————————\n\e[36m$\e[0m ' -set -x - -source $THE_SCRIPT 2>&1 | tee $OUTFILE -sed -i -e's/\x1b/␛/g' $OUTFILE - -rm -rf $_tempdir -rm -rf $_confdir diff --git a/lessons/git/git-collaboration-2in1/index.md b/lessons/git/git-collaboration-2in1/index.md deleted file mode 100644 index f96ecdb8..00000000 --- a/lessons/git/git-collaboration-2in1/index.md +++ /dev/null @@ -1,331 +0,0 @@ -# Spolupráce a Git - -„Opravdové” programy zřídka vznikají prací jednoho člověka. Víc hlav víc ví, a tak je dobré si na projekt vytvořit tým. -Každý člen týmu potřebuje mít přístup k práci ostatních. Jak ale zařídit to, aby několik lidí mohlo najednou upravovat -stejné části (soubory) programu? - -Určitě jste někdy použili nějaký nástroj na sdílení souborů jako je Dropbox nebo OneDrive. -Tyto nástroje se většinou moc neumí vyrovnat s tím, když se změní jeden soubor najednou na dvou počítačích. -Většinou vytvoří 2 konfliktní kopie a je na uživatelích, aby našli v souborech změny a spojili je. - -Takový způsob práce není ve velkém projektu s mnoha programátory udržitelný. -Kód je většinou v mnoha souborech a přidání jedné nové funkcionality může vyžadovat změnu ve více souborech. -Jen si představte jakou paseku by nadělalo pět programátorů v jedné Dropbox složce. - -Zdrojový kód se dá poměrně dobře sdílet a přepoužívat, pokud je napsaný podle určitých pravidel. -Spolupráce a sdílení kódu je spjato s programováním od nepaměti, už od prvních počítačů. -Tohle přepoužívání se postupně vyvinulo do fenoménu *open-source* a *svobodného softwaru*. - -Dnes si vyzkoušíme, jak spolupracovat s mnoha programátory na jednom projektu pomocí verzovacího nástroje *Git* a služby *GitHub*. - -## Git a GitHub - -Představte si, že jste už napsali větší Python program, který je už kvůli složitosti a velikosti rozdělen do více souborů. -Takové složce s kódem říkáme repozitář. -Git je program, který se stará o to, abyste ve složce mohli dělat různé experimenty a vrátit se třeba ke starým verzím. -Každá verze má ale popis, který jste jí sami dali, takže slouží i jako dobrá dokumentace projektu. - -A co víc – Git vám dovoluje sdílet kód s ostatními programátory. -Spousta míst na Internetu funguje tak, že vybraná skupina lidí má „přístup”: můžou dělat změny, jak se jim líbí. -S Gitem se používá jiný model: změny nahrajeme do vlastního sdíleného repozitáře -a majiteli původního projektu napíšeme žádost o začlenění těch změn (angl. pull request). -Může to být třeba mail se slovy „Hele, na té a té adrese mám nějaké změny, které by se ti mohly hodit! Přidej je do svého projektu!” - -Výhoda je v tom, že se do projektu ‒ pokud je veřejný ‒ může zapojit kdokoliv. -Přispěvatel se nemusí předem ptát, nemusí dokazovat že je důvěryhodná osoba, stačí něco změnit a poslat. -Jestli se změna bude autorům projektu líbit nebo ne, to už je jiná věc. -Ale záleží hlavně na samotné změně, ne na tom, kdo ji udělal. -Většina projektů v README obsahuje návod, jak do projektu přispět (angl. Contribution Guidelines). -Zkuste se po nich podívat, ušetříte práci sobě i autorům. - -Služba [GitHub](https://github.com) umožňuje vytvořit si vlastní sdílený gitový repozitář a zjednodušuje začleňování změn -(místo posílání mailů stačí zmáčknout tlačítko). - -Podobných služeb existuje víc (např. bitbucket.org, gitlab.com). Všechny fungují podobně; GitHub je momentálně nejpopulárnější. - -Kdybyste měli v různých kopiích repozitáře zmatek, přijde vhod malé vysvětlení: jedna kopie je původní projekt na GitHubu, -kam správce projektu dává aktuální „oficiální verzi”. -Další kopie na GitHubu je „vaše” a můžete si do ní nahrát cokoliv -(nejčastěji se v ní ale zveřejňují změny, které můžou být užitečné pro ostatní). -A třetí kopii repozitáře máte u sebe na počítači. - -![Git workflow](static/gh-workflow-diagram.svg) - -### Práce s lokálním repozitářem - -Než budeme schopní poslat nějaké změny k začlenění do našeho vybraného projektu, musíme se naučit, jak se s Gitem pracuje. -Co je to vlastně ten zmíněný *repozitář*? Můžeme ho vytvořit z jakéhokoliv adresáře na disku, kde máme uložené soubory, -jen Gitu řekneme, že z něj chceme ten repozitář udělat. To má za následek: - -1. Git bude sledovat změny, které ve složce uděláme. -2. Budeme schopni vytvořit verzi tohoto adresáře, ke které se můžeme kdykoliv vrátit. -3. Můžeme nastavit vzdálené repozitáře, kam chceme změny nahrávat nebo naopak stahovat změny od jiných lidí. - -> [note] Pozor -> Budeme hodně pracovat s příkazovou řádkou. Jestli se s ní ještě nekamarádíš, koukni se na [úvod](../../beginners/cmdline/). -> Nezapomeň: $ na začátku se nepíše; je tu proto, aby šlo poznat, že jde o příkaz. - -Naším dnešním cílem je ale přispět do projektu, který založil někdo jiný. Proto použijeme příkaz `git clone` ke stažení vzdáleného repozitáře. - -```console -$ git clone https://github.com/asgeirrr/prezencka -``` - -V aktuálním adresáři se nám vytvořil podadresář `prezencka`, který obsahuje všechny soubory, které vidíme ve webovém rozhraní na [GitHubu](https://github.com/asgeirrr/prezencka). - - -#### Stav repozitáře (`git status`) - -Velmi často potřebujeme zjistit současný stav repozitáře. Navigujte v příkazové řádce do adresáře `prezencka` a zadejte `git status`. Git nám odpoví: - -```console -$ git status -On branch master -Your branch is up-to-date with 'origin/master'. -nothing to commit, working tree clean -``` - -Git nám v tuto chvíli poskytl 3 informace: - -1. Že jsme na větvi `master`. Git umožňuje pracovat na více věcích současně pomocí tzv. *větví* mezi kterými můžeme přepínat. Pak se na na disku „objeví” verze souborů v dané větvi. My si dnes vystačíme s jednou hlavní větví, která se tradičně jmenuje `master`. -2. Že naše větev `master` je aktuální vůči větvi na vzdáleném repozitáři (origin) na GitHubu. -3. Že jsme neudělali žádné změny oproti poslední verzi souborů, což je pravda. - -#### První revize (`git commit`) - -Už jsme několikrát zmínili, že Git je verzovací systém. Jak že to ale soubory verzuje? -V podstatě mu musíme dát příkaz, aby zmrazil současný stav repozitáře, tzv. vytvořit *revizi*. -Pak můžeme dělat další změny, ale už vždy budeme schopni se k této revizi vrátit. - -Přidejte do adresáře `prezencka` soubor `vase_jmeno.txt`, např. tedy `magdalena_rettigova.txt` -a do něj napište svoje současné povolání nebo cokoliv jiného. -Zkontrolujte současný stav repozitáře pomocí `git status`: Git oznámí, že v adresáři je soubor, o kterém ještě „neví“. - -```console -$ git status -On branch master -Your branch is up-to-date with 'origin/master'. -Untracked files: - (use "git add <file>..." to include in what will be committed) - - magdalena_rettigova.txt - -nothing added to commit but untracked files present (use "git add" to track) -``` - -U každého nového souboru musíme Gitu říct, že chceme jeho obsah sledovat. Proveďte to se svým souborem: - -```console -$ git add magdalena_rettigova.txt -``` - -a znovu zkontrolujte stav repozitáře: - -```console -$ git status -On branch master -Your branch is up-to-date with 'origin/master'. -Changes to be committed: - (use "git reset HEAD <file>..." to unstage) - - new file: magdalena_rettigova.txt -``` - -To, co je zelené („changes to be committed“), se přidá do další revize (angl. commit), kterou vytvoříme. Pojďme tedy vytvořit revizi: - -```console -$ git commit -``` - -Po zadání tohoto příkazu se otevře editor, do kterého je nutné napsat výstižný popis změn, -abyste vy i ostatní programátoři věděli, co tahle revize obsahuje za změny. -Např. tedy `Přidáno mé jméno a povolání`. Předvyplněné řádky začínající # můžeme nechat být -(nebo vymazat, podle chuti – Git je ignoruje). -Pak soubor uložíme a zavřeme editor. - -```console -$ git status -On branch master -Your branch is ahead of 'origin/master' by 1 commit. - (use "git push" to publish your local commits) -nothing to commit, working tree clean -``` - -Pro lepší pochopení, co dělají jednotlivé příkazy a v jakém stavu můžou být soubory/změny, přikládáme tento diagram: - -![Git workflow](static/diagram.png) - -#### Log (`git log`) -Teď, když za sebou máme první revizi, podívejme se, jak vypadá historie repozitáře, do kterého chceme přispívat. -K tomu můžeme použít příkaz `git log`, jako první uvidíme naši poslední změnu a další jsou změny od jiných autorů, -na které navazujeme. - -```console -$ git log -commit 0bdfbb2a2398fea179395a8dd303e9f672ef4dca -Author: Magdalena Dobromila <magdalena@rettigova.cz> -Date: Tue Mar 28 19:27:08 2017 +0200 - - Přidáno mé jméno a povolání - -commit 0f305972803131cb6c8637359fee8ede3005bba6 -Merge: effa89b 175f4cc -Author: Oskar Hollmann <oskar@hollmann.me> -Date: Tue Nov 8 20:17:00 2016 +0100 - - Merge pull request #1 from muzikovam/master - - Tady mas jmeno -``` - -V logu se pohybujeme šipkami a když z něj chceme vyskočit zpět, stiskneme `q` jako quit. -Příkaz `git log` má mnoho přepínačů, přehlednější historii změn můžeme získat pomocí `git log --oneline --graph --decorate`. - -## Open-source a Free software - -Dejme si menší pauzu na zažití základní práce s lokálním repozitářem. -Nejde mluvit o Gitu a spolupráci a nezastavit se chvíli u otevřeného zdrojového kódu. -První kód vznikal v akademické sféře, kde byl zcela přirozeně sdílen, jako je to s poznatky mezi vědci běžné. -50\. a 60. léta byla obdobím velké kreativity, kdy vzniklo mnoho z konceptů a technologií, které dnes používáme. -Pak se začalo programování postupně komercializovat a firmy začaly zdrojový kód skrývat jako konkurenční výhodu. -Do té doby víceméně jednolitá komunita programátorů byla nucena se rozdělit. - -Některým programátorům tohle skrývání kódu hluboce vadilo až roku 1985 publikoval -[Richard Stallman](https://en.wikipedia.org/wiki/Richard_Stallman) GNU Manifesto, -kde vysvětlil, proč hodlá vytvořit operační systém s otevřeným kódem a odstartoval tak hnutí svobodného softwaru. -To prosazuje 4 následujících svobody (převzato z [Wikipedie](https://cs.wikipedia.org/wiki/Svobodn%C3%BD_software)): - -1. svoboda používat program za jakýmkoliv účelem, -2. svoboda studovat, jak program pracuje a možnost přizpůsobit ho svým potřebám, -3. svoboda redistribuovat kopie programu, -4. svoboda vylepšovat program a zveřejňovat zlepšení, aby z nich mohla mít prospěch celá komunita. - -Dnes je spousta projektů s otevřeným zdrojovým kódem dostupných na Internetu a každý je používáme. -Jejich další sdílení je upraveno jednou z licencí, které tyto základní svobody zaručují. - -> [note] Pozor -> Termíny **open-source** a **free software** nejsou zcela zaměnitelné, ale pro naše účely je zatím můžeme chápat jako synonyma. - -Spoustu open-source projektů najdete právě na GitHubu. Ne všechny jsou v Pythonu. Ne všechny jsou kvalitní – -protože si každý může zveřejnit co chce, na GitHubu se válí spousta nedodělků, opuštěných nápadů a nepodařených experimentů. -A bohužel, ne všechny projekty mají přátelské autory. - -Na druhou stranu ale open-source programy mají svoje výhody: nejenom se z nich může kdokoli učit, -ale každý může i zkontrolovat, jestli dělají to, co dělat mají. -Populární open-source programy nás například pravděpodobně nebudou špehovat (tj. hlásit autorovi, co na počítači děláme), -ani většinou neobsahují reklamy: kdyby to dělaly, najde se někdo kdo tyhle „funkce” odstraní -a lidé časem začnou používat opravenou verzi. - -Některé příklady populárních open-source projektů: - -* Mozilla Firefox, Chromium (prohlížeče) -* Atom, gedit (textové editory) -* CPython (jazyk Python) -* Linux, Android (jádra operačních systémů) -* Pytest (pythonní knihovna na testování) -* Django, Flask, Requests (webové knihovny pro Python) -* NumPy, Jupyter, Matplotlib (pythonní knihovny pro vědce a analytiky) -* Materiály k tomuto kurzu - -A jak vidno z posledního příkladu, nejen softwarové projekty se dají vést takhle veřejně. Tento kurz vychází z principů open source: -všechno know-how je sdílené a budeme rádi, když se zapojíte. - -Pokud vás tato problematika zajímá, doporučujeme krátkou knihu [Katedrála a tržiště](https://www.root.cz/knihy/katedrala-a-trziste/), která nabízí srovnání open-source s tradičním vývojem softwaru za zavřenými dveřmi. - -Pokud budete někdy začínat nový projekt a budete chtít, aby z něj měl prospěch někdo další, -stojí za to uvolnit ho pod svobodnou licencí a dát třeba právě na GitHub. -Licence projektu většinou najdete v souboru `LICENCE`. -Na kód neuvolněný pod svobodnou licencí se automaticky vztahuje copyright a ostatní ho nemohou volně sdílet. - -Informace o open-source licencích najdete např. na [choosealicence.com](https://choosealicense.com), případně [creativecommons.org](https://creativecommons.org) a [opensource.org](https://opensource.org). - -## Spolupráce na projektu - -Nyní se posuneme ke sdílení revize, kterou jsme vytvořili, a ke stažení revizí od ostatních přispěvatelů. - -### Posílání změn (`git push`) - -Nyní stačí požádat autora projektu, aby naše změny začlenil do repozitáře na GitHubu -a všichni uživatelé projektu, na kterém pracujeme, budou mít užitek z našich změn. -Nemáme ale do původního repozitáře práva na zápis, musíme tedy poslat změny nejprve do naší kopie repozitáře, -vizte obrázek na začátku. - -Udělejte si účet na GitHubu (jestli ho ještě nemáte) a pak jděte na adresu [prezencky](https://github.com/asgeirrr/prezencka/), -kterou jste použili pro `git clone`. -Vpravo nahoře je tlačítko „Fork”, klikněte na něj. Tím se na GitHubu vytvoří vaše kopie repozitáře: -adresa by měla být něco jako `https://github.com/tvojejmeno/prezencka`. - -A teď, jak nahrát změny z našeho počítače na GitHub? Git si u každého repozitáře na našem počítači pamatuje adresy, -odkud se dají stahovat a kam se dají posílat změny. -Seznam těchto adres nám ukáže příkaz `git remote -v`. Třeba: - -```console -$ git remote -v -origin https://github.com/asgeirrr/prezencka (fetch) -origin https://github.com/asgeirrr/prezencka (push) -``` - -Tenhle výstup znamená, že pod zkratkou *origin* se schovává adresa, ze které jsme repozitář naklonovali. - -Přidejme si podobnou zkratku pro vlastní repozitář na GitHubu. Můžeme ho pojmenovat např. *moje* -nebo svým uživatelským jménem na GitHubu, -aby nám bylo jasné, že je to ten náš, do kterého můžeme nahrávat změny. - -```console -$ git remote add tvojejmeno https://github.com/tvojejmeno/prezencka -``` - -Zkontrolujme, že se nám to povedlo: - -```console -$ git remote -v -origin https://github.com/asgeirrr/prezencka (fetch) -origin https://github.com/asgeirrr/prezencka (push) -tvojejmeno https://github.com/tvojejmeno/prezencka (fetch) -tvojejmeno https://github.com/tvojejmeno/prezencka (push) -``` - -Tolik k nastavení ‒ `git remote add` stačí udělat jednou pro každý repozitář. Pak už můžeme změny nahrávat pomocí: - -```console -$ git push tvojejmeno master -``` - -což znamená: pošli na adresu uloženou pod zkratkou `tvojejmeno` větev `master`. - -Funguje? Můžeme se podívat na `https://github.com/tvojejmeno/prezencka` v prohlížeči a ujistit se, že tam změny jsou. - - -### Žádost o začlenění (pull request) - -Teď zbývá požádat autory původního projektu, aby změny z tvého sdíleného repozitáře přidali do svojí kopie. -GitHub na to má vlastní mechanismus zvaný *pull request* (žádost o začlenění). - -Na stránce původního projektu (na adrese, kterou jsme použili na začátku pro `git clone`) -by mělo být oznámení o nově nahrané větvi s velkým zeleným tlačítkem `Compare & pull request`. -Po kliknutí na tlačítko můžeme doplnit popis toho, co tahle změna obnáší, a pak zmáčkneme další tlačítko. - -Hotovo; teď je na autorech projektu, aby se na změny podívali a přijali ‒ nebo začali diskusi o tom, jak ji ještě vylepšit. -(Diskutovat se dá na stránce pull requestu nebo přes mail.) - -> [note] Poznámka -> Procházíte-li materiály z domu, musíte počkat, než si někdo žádosti všimne a začlení ji. -> To může trvat i pár dní; kdyby to bylo přes týden, tak se zkuste se ozvat znovu. - - -### Aktualizace (`git pull`) - -Když budou změny od všech účastníků začleněné, můžeme si aktualizovat repozitář, který máme u sebe na počítači. Příkaz - -```console -$ git pull origin master -``` - -stáhne změny z větve „master” z adresy pod zkratkou „origin”. -Pomocí `git log` se můžeme podívat, jak se projekt mezitím vyvinul. - -Kruh se uzavřel, jsme schopni začlenit do projektu vlastní změny a naopak si stáhnout změny od ostatních. -Až příště uvidíte chyby v materiálech nebo nešikovnou formulaci, zkuste to opravit a udělat pull request. - -Git je velké téma a mohli bychom probírat větvení nebo řešení konfliktů a spoustu dalšího, -ale nechme si to na pokračovací srazy nebo workshop. diff --git a/lessons/git/git-collaboration-2in1/info.yml b/lessons/git/git-collaboration-2in1/info.yml deleted file mode 100644 index dfac370b..00000000 --- a/lessons/git/git-collaboration-2in1/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Spolupráce a Git -style: md -attribution: Pro PyLadies CZ napsali Petr Viktorin a Oskar Hollman, 2015-2017. -license: cc-by-sa-40 diff --git a/lessons/git/ignoring/index.md b/lessons/git/ignoring/index.md deleted file mode 100644 index ff965eb3..00000000 --- a/lessons/git/ignoring/index.md +++ /dev/null @@ -1,308 +0,0 @@ -# Ignorování souborů - -Často se stává, že některé soubory v repozitáři nechceš. -Takových souborů jsou tři hlavní druhy: - -Pomocné soubory nástrojů -: Python občas „sám od sebe“ vytváří adresář `__pycache__` s pomocnými - soubory, aby zrychlil importování modulů. - Některé počítače vytváří skryté soubory s názvy jako - `.Thumbnails`, `.DS_Store` nebo `Thumbs.db`. - Takové věci v repozitáři nemají co dělat – je - dobrým zvykem do Gitu nedávat nic, co jde vytvořit automaticky. - -Výstup programu a nastavení -: Píšeš-li program, který kreslí obrázky, většinou chceš v repozitáři - jen samotný program. - Obrázky si může pomocí programu každý vytvořit sám. -: Podobně fungují soubory s heslem: pokud program potřebuje heslo - např. k nějaké webové službě, ale svoje heslo nechceš dávat veřejně - k dispozici, musí si každý vytvořit soubor s heslem sám. - -Osobní soubory -: Občas se stane, že v adresáři s repozitářem máš soubor s osobními - poznámkami. - Zbytek repozitáře plánuješ zveřejnit, ale tyto soubory by měly zůstat - jen ve tvé kopii. A to včetně informace o tom, že takové soubory máš. - -Adresář s virtuálním prostředím -: Jistě už sis zvykl{{a}} na virtuální prostředí. - Adresář s ním se může jmenovat různě, v začátečnickém kurzu používáme název `venv`. - Není dobré tento adresář dávat do Gitu, - protože je jednoduché jej vždy vytvořit znovu - a pokud na projektu spolupracuje více lidí - (nebo ty z více počítačů), mohlo by dělat neplechu, kdyby virtuální - prostředí nebylo vždy na úplně stejném místě. - Virtuální prostředí z adresáře `/home/helena/projektABC/venv` - nebude fungovat z adresáře `C:\Users\Helena\projektABC\venv`, - ale ani z `/home/mirka/projektABC/venv`. - -My budeme chtít Git nastavit tak, aby tyto soubory ignoroval: aby -`git status` neukazoval červeně, že ještě nejsou v repozitáři. - -## Příprava - -Pojďme si to ukázat na příkladu. -Založ si nový repozitář a vytvoř v něm tři soubory s tímto obsahem: - -* `obrazek.py` - - ```python - from turtle import forward, left, right, getcanvas - - forward(50) - left(60) - forward(50) - right(60) - forward(50) - - getcanvas().postscript(file='obrazek.ps') - ``` - -* `poznamky.txt` - - ```plain - Tohle je tajné! - ``` - -* `Autofile.tmp` - - Do tohohle souboru napiš cokoliv. - Různé operační systémy a (jiné programy) vytváří různé soubory - podivných jmen; `Autofile.tmp` pro nás bude představovat takový - automaticky vzniklý soubor. - -Pythonní program spusť (pomocí `python obrazek.py`). -Mělo by se na chvíli ukázat okno s želvou a měl by vzniknout nový soubor -`obrazek.ps`. - -> [note] -> Obrázek ve formátu PostScript (.ps) se dá otevřít ve většině programů, které -> zvládají i PDF, případně v [Inkscape](https://inkscape.org/). - -Jak se na to dívá Git? - -```ansi -␛[36m$␛[0m git status -On branch master - -No commits yet - -Untracked files: - (use "git add <file>..." to include in what will be committed) - ␛[31mAutofile.tmp␛[m - ␛[31mobrazek.ps␛[m - ␛[31mobrazek.py␛[m - ␛[31mpoznamky.txt␛[m - -nothing added to commit but untracked files present (use "git add" to track) -``` - -Spousta souborů, ale jen jeden z nich chceš v repozitáři. -Co s těmi ostatními? - - -## Výstupy programu a pomocné soubory společných nástrojů - -Nejjednodušší je vyrovnat se se souborem `obrazek.ps`. -V repozitáři ho nechceš (je to repozitář *zdrojového* kódu; výsledky bývá -lepší schraňovat jinde než v Gitu). -Zároveň víš, že každý, kdo s repozitářem bude pracovat, pravděpodobně -tenhle soubor vytvoří. -Bylo by tedy dobré říct *všem* lidem, kteří se k repozitáři dostanou, že tento -soubor do Gitu nepatří. -To se dělá záznamem ve speciálním souboru v repozitáři. - -Udělej soubor s názvem `.gitignore`. -(Pozor na tečku ve jménu souboru; na některých systémech se špatně zadává – -doporučuji soubor vytvořit v programátorském editoru.) -Do něj napiš: - -```plain -obrazek.ps -``` - -Pak se podívej na `git status`. Obrázek už by ve výpisu neměl být! - -```ansi -␛[36m$␛[0m git status -On branch master - -No commits yet - -Untracked files: - (use "git add <file>..." to include in what will be committed) - ␛[31m.gitignore␛[m - ␛[31mAutofile.tmp␛[m - ␛[31mobrazek.py␛[m - ␛[31mpoznamky.txt␛[m - -nothing added to commit but untracked files present (use "git add" to track) -``` - -Nový soubor `.gitignore` přidej do repozitáře společně se samotným programem: - -```ansi -␛[36m$␛[0m git add .gitignore obrazek.py -␛[36m$␛[0m git status -On branch master - -No commits yet - -Changes to be committed: - (use "git rm --cached <file>..." to unstage) - ␛[32mnew file: .gitignore␛[m - ␛[32mnew file: obrazek.py␛[m - -Untracked files: - (use "git add <file>..." to include in what will be committed) - ␛[31mAutofile.tmp␛[m - ␛[31mpoznamky.txt␛[m -``` - -Když uděláš `git commit` a repozitář nasdílíš s ostatními, všichni dostanou -`.gitignore` s instrukcí, že `obrazek.ps` do repozitáře nepatří. - -Jak ignorovat zbylé dva soubory? - - -## Osobní poznámky - -Soubor `poznamky.txt` se taky dá zařadit do `.gitignore`, ale moc se tam nehodí. -Existuje jen u tebe; není důvod předpokládat, že si někdo jiný vytvoří -soubor se stejným jménem. - -Dej ho tedy do souboru, který se nebude šířit s repozitářem. -Tento soubor je `.git/info/exclude`. -(Může být trochu složité ho najít, protože adresář `.git` je skrytý. -Nevidíš–li ho, napiš v editoru do okýnka pro otevření souboru `.git` a dostaneš -se do něj.) - -Soubory v adresáři `.git` bys neměl{{a}} měnit, protože se tak dá přijít -o historii projektu. -Ale `exclude` je výjimka. Napiš na konec tohoto souboru: - -```plain -poznamky.txt -``` - -A po uložení budou poznámky ignorovány! - -```ansi -␛[36m$␛[0m git status -On branch master - -No commits yet - -Changes to be committed: - (use "git rm --cached <file>..." to unstage) - ␛[32mnew file: .gitignore␛[m - ␛[32mnew file: obrazek.py␛[m - -Untracked files: - (use "git add <file>..." to include in what will be committed) - ␛[31mAutofile.tmp␛[m -``` - -## Další haraburdí - -Některé programy automaticky vytvářejí pomocné soubory. -Často to dělají správci souborů (často `.Thumbnails` na Linuxu, -`.DS_Store` na Macu nebo `Thumbs.db` na Windows). -Některé editory si taky nechávají na disku nastavení. - -Podobné soubory se dají dát do `.gitignore`. -Je ale lepší si je dát do osobního nastavení, protože ostatní lidé, -kteří na projektu můžou spolupracovat, nemusí používat stejný systém -a programy. - -> [note] -> Pokud si můžeš být jist{{gnd('ý', 'á')}}, že ostatní budou používat právě -> ten program, který používáš ty, použij `.gitignore`. -> Příklad je adresář `__pycache__`, který vytváří Python při importu modulu. - -Soubor s osobním nastavením si můžeš pojmenovat, jak chceš, a můžeš ho uložit -kde budeš chtít. -Já doporučuji ho pojmenovat `.gitignore_global` a dát ho do tvého domovského -adresáře. - -Do souboru zase napiš jméno ignorovaného souboru: - -```plain -Autofile.tmp -``` - -Potom řekni Gitu, kde tento soubor najít: - -```ansi -␛[36m$␛[0m git config --global core.excludesfile /tmp/tmp.1spGPvBL5W/.gitignore_global -``` - -A měl by být ignorován: - -```ansi -␛[36m$␛[0m git status -On branch master - -No commits yet - -Changes to be committed: - (use "git rm --cached <file>..." to unstage) - ␛[32mnew file: .gitignore␛[m - ␛[32mnew file: obrazek.py␛[m -``` - - -## Formát ignorovacího souboru - -Ve všech třech „ignorovacích“ souborech lze samozřejmě uvést víc souborů: -každý na zvláštní řádek. -Kromě toho je možné použít několik vychytávek: - -* `*` ve jméně souboru nahradí část jména souboru. - Takže pokud chceš ignorovat všechny soubory s příponou `.tmp`, můžeš napsat: - - ```plain - *.tmp - ``` - -* `/` na konci jména značí adresář. Chceš-li tedy ignorovat adresáře - `__pycache__` (což v Pythonním projektu chceš), napiš do `.gitignore`: - - ```plain - __pycache__/ - ``` - -Další detaily je možné najít v [dokumentaci](https://git-scm.com/docs/gitignore). - - -## Automatické přidávání - -Teď, když umíš ignorovat soubory, si můžeme ukázat zkratku. Místo - -```console -$ git add soubor1 soubor2 -$ git commit -``` - -můžeš napsat jen `git commit` s tečkou na konci: - -```console -$ git commit . -``` - -To automaticky přidá *všechny* neignorované soubory, které `git status` ukazuje -červeně. -Tečka je jméno pro aktuální adresář – celý adresář a všechno pod ním se přidá -do revize. - -Doporučuji si před použitím téhle zkratky zkontrolovat `git status`, aby sis -ověřil{{a}}, že nepřidáváš nic, co nechceš. - -Taky doporučuji si nastavit Git, aby se v editoru, kam píšeš popisek revize, -ukazovala poznámka s tím, co vlastně v nové revizi bude. -Uvidíš tak něco jako `git status` vždy, když začneš psát popisek k revizi: - -```console -$ git config --global commit.verbose 1 -``` diff --git a/lessons/git/ignoring/info.yml b/lessons/git/ignoring/info.yml deleted file mode 100644 index 69eada10..00000000 --- a/lessons/git/ignoring/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Ignorování souborů -style: md -attribution: Pro PyLadies CZ napsal Petr Viktorin, 2017. -license: cc-by-sa-40 diff --git a/lessons/git/ignoring/script.sh b/lessons/git/ignoring/script.sh deleted file mode 100644 index 81f91c13..00000000 --- a/lessons/git/ignoring/script.sh +++ /dev/null @@ -1,54 +0,0 @@ -git init - -cat > obrazek.py << END -from turtle import forward, left, right, getcanvas - -forward(50) -left(60) -forward(50) -right(60) -forward(50) - -getcanvas().postscript(file='obrazek.ps') -END - -cat > poznamky.txt << END -Tajné! -END - -cat > Autofile.tmp << END -Blablabla -END - -python obrazek.py -git status - -cat > .gitignore << END -__pycache__/ -*.pyc - -obrazek.ps -END - -git status -git add .gitignore obrazek.py -git status - - -cat > .git/info/exclude << END -/poznamky.txt -END - -git status - -cat > ~/.gitignore_global << END -Autofile.tmp -END - -git config --global core.excludesfile ~/.gitignore_global - -git status - - -GIT_EDITOR='echo "První revize" >' git commit -git status diff --git a/lessons/git/install/index.md b/lessons/git/install/index.md deleted file mode 100644 index 75e3ddf5..00000000 --- a/lessons/git/install/index.md +++ /dev/null @@ -1,139 +0,0 @@ -# Git - -Další program, který budeme později potřebovat, -nám později umožní (mimojiné) spolupracovat -na vznikajících programech s ostatními. -Jmenuje se Git. -Pojďme si ho nainstalovat a nastavit. - -Instalace je různá pro různé operační systémy, vyber ten svůj. - - -## Linux - -Instalaci na Linux zvládneme jedním příkazem: - -**Fedora, RHEL**: - -```console -$ sudo dnf install git gitk git-gui nano -``` - -**Ubuntu, Debian**: - -```console -$ sudo apt-get install git gitk git-gui nano -``` - -U jiných Linuxů předpokládám, že instalovat umíš; nainstaluj si *git*, -*gitk*, *git gui* a *nano*. - -Jestli máš nainstalováno, ještě nastav Gitu editor. -Pokud nemáš rád{{a}} Vim (nebo nevíš co to je), -zadej tento příkaz: - -```console -$ git config --global core.editor nano -``` - -Dál pokračuj obecným [nastavením](#config) níže. - - -## Windows - -Jdi na stránku [git-scm.org](https://git-scm.org), stáhni si -Git a nainstaluj si ho. - -Při instalaci se ujisti, že jsou vybrány tyto volby: - -* Adjusting your PATH enviroment: Git from the command line and also from 3rd-party software -* Configuring the line ending conversions: Checkout Windows-style, commit Unix-style line endings - -Ostatní možnosti neměň. - -Potom Gitu nastav editor. -Máš-li otevřenou příkazovou řádku, zavři ji a otevři novou. -(Instalace mění systémové nastavení, které se musí načíst znovu.) -V nové příkazové řádce zadej: - -```console -> git config --global core.editor notepad -> git config --global format.commitMessageColumns 80 -> git config --global gui.encoding utf-8 -``` - -A teď pokračuj v sekci [Nastavení](#config) níže – macOS přeskoč. - - -## macOS - -Spusť v příkazové řádce `git`. -Je-li už nainstalovaný, dozvíš se, jak ho používat -(výpis začíná `usage`). -Jinak ho nainstaluj pomocí Homebrew: - -```console -$ brew install git git-gui -``` - -Nainstalovanému Gitu je ještě potřeba nastavit editor (zadej `nano`, -i když sis v rámci instalace editoru nainstaloval{{a}} např. Atom). -Dělá se to tímto příkazem: - -```console -$ git config --global core.editor nano -``` - -Dál pokračuj obecným nastavením: - - -{{ anchor('config') }} -## Nastavení - -Na projektu, který bude uložen v Gitu, může -spolupracovat více lidí. -Aby šlo dohledat, kdo udělal kterou změnu, je Gitu -potřeba říct jméno a e-mail. -Do příkazové řádky zadej následující příkazy, změň v nich ale -jméno a adresu: - -```console -$ git config --global user.name "Adéla Novotná" -$ git config --global user.email adela.novotna@example.cz -``` - -Můžeš samozřejmě použít i přezdívku, nebo dokonce -falešný e-mail, ale v takovém případě bude složitější se -zapojit do týmových projektů. -Každopádně, jméno i e-mail jdou kdykoli změnit -tím, že konfigurační příkazy zadáš znovu. - -> [note] -> Pokud se bojíš spamu, neboj: nezačneš ho dostávat víc -> než při normálním používání e-mailu. -> Adresa se zobrazí jen lidem, kteří si stáhnou projekt, -> do kterého jsi přispíval{{a}}. -> Spammeři se většinou zaměřují na méně technicky zdatné -> lidi, než jsou uživatelé Gitu. :) - -Dále si můžeš nastavit barevné výpisy – pokud si tedy -(jako někteří autoři Gitu) nemyslíš, že příkazová -řádka má být černobílá: - -```console -$ git config --global color.ui true -``` - -> [note] -> Spuštění `git config` nevypíše žádnou hlášku, že se operace povedla. -> To je normální; stejně se chová spousta dalších příkazů, např. `cd`. -> -> Aktuální konfiguraci gitu si můžeš zkontrolovat příkazem: -> -> ```console -> $ git config --global --list -> user.name=Adéla Novotná -> user.email=adela.novotna@example.cz -> ``` - -A to je vše! Git máš nainstalovaný. Gratuluji! diff --git a/lessons/git/install/info.yml b/lessons/git/install/info.yml deleted file mode 100644 index 3c06bc17..00000000 --- a/lessons/git/install/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Instalace Gitu -style: md -attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017 -license: cc-by-sa-40 diff --git a/lessons/github-api/index.html b/lessons/github-api/index.html new file mode 100644 index 00000000..7a4bd959 --- /dev/null +++ b/lessons/github-api/index.html @@ -0,0 +1,147 @@ +<h1 id="webove_api">Webové API +<a href="#webove_api" class="header-link">#</a> +</h1> +<p>Jak už bylo řečeno v <a href="naucse:page?lesson=intro/json">lekci o JSON</a>, +hlavní výhoda formátu JSON je, že se na Internetu rozšířil nejvíc. +Pojďme toho využít!</p> +<p>Spousta webových služeb poskytuje takzvané +<em>API</em> (z <em>application programming interface</em>, +programátorské rozhraní), přes které je možné s danou +službou komunikovat programově. +Místo klikání na tlačítka a čtení stránek „očima” +dostaneš data ve formátu, kterému rozumí počítače. +V dnešní době to bude většinou formát JSON.</p> +<p>Z minulých lekcí bys měl/a mít založený účet na github.com. +Podívejme se na to, jak se zeptat Githubu, co o tobě ví.</p> +<h2 id="autorizace">Autorizace +<a href="#autorizace" class="header-link">#</a> +</h2> +<p>První, a mnohdy nejsložitější, krok k použití API +je přihlášení. Počítače se totiž přihlašují jinak +než lidi a problematika bezpečnosti a oprávnění by vydala na samostatný kurz. +My to uděláme co nejjednodušeji, ať se rychle dostaneme k jádru věci:</p> +<ul> +<li>Přihlaš se na <a href="https://github.com">github.com</a>.</li> +<li>Jdi na <a href="https://github.com/settings/tokens">nastavení Personal Accesss Tokens</a>.</li> +<li>Vytvoř si nový token ("Generate new token"). Nezaškrtávej žádná oprávnění navíc.</li> +<li>Zkopíruj si token (dlouhý řetězec), které takto dostaneš, do souboru <code>token.txt</code>.</li> +</ul> +<div class="admonition warning"><p class="admonition-title">Pozor!</p> +<p>Vygenerovaný token je heslo, které držitele +opravňuje pracovat s Githubem pod tvým jménem! +Drž ho v tajnosti. Kdyby se přece jen dostalo „ven”, na stránce +<a href="https://github.com/settings/tokens">Personal Accesss Tokens</a> ho deaktivuj.</p> +</div><p>Obsah tokenu se ti zobrazí pouze jednou, hned po vytvoření. +Když ho zapomeneš, deaktivuj ho a vytvoř si nový.</p> +<h2 id="requests">Requests +<a href="#requests" class="header-link">#</a> +</h2> +<p>K práci s internetovými stránkami použijeme knihovnu Requests. +V aktivovaném virtuálním prostředí si ji nainstaluj příkazem:</p> +<div class="highlight"><pre><span></span><span class="gp gp-VirtualEnv">(venv)</span><span class="gp">$ </span>python<span class="w"> </span>-m<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>requests +</pre></div><p>A potom v Pythonu zkus stáhnout nějakou stránku:</p> +<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">requests</span> + +<span class="c1"># stažení stránky</span> +<span class="n">stranka</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'https://github.com'</span><span class="p">)</span> + +<span class="c1"># ověření, že dotaz proběhl v pořádku</span> +<span class="n">stranka</span><span class="o">.</span><span class="n">raise_for_status</span><span class="p">()</span> + +<span class="c1"># vypsání obsahu</span> +<span class="nb">print</span><span class="p">(</span><span class="n">stranka</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> +</pre></div><p>Měl by se vypsat obsah stránky +<a href="https://github.com">https://github.com</a> – +HTML kód, který se objeví když v prohlížeči dáš +„Ukázat zdroj” (<em>View Page Source</em>, většinou <kbd>Ctrl</kbd>+<kbd>U</kbd>) +a ze kterého prohlížeč umí vykreslit stránku.</p> +<p>Ale my nechceme obsah pro lidi. +Podívejme se, co Github zpřístupňuje počítačům.</p> +<h2 id="uzivatelsky_ucet">Uživatelský účet +<a href="#uzivatelsky_ucet" class="header-link">#</a> +</h2> +<p>Zkus, co dělá tento kód:</p> +<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">requests</span> + +<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'token.txt'</span><span class="p">)</span> <span class="k">as</span> <span class="n">soubor</span><span class="p">:</span> + <span class="n">token</span> <span class="o">=</span> <span class="n">soubor</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> + +<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'Authorization'</span><span class="p">:</span> <span class="s1">'token '</span> <span class="o">+</span> <span class="n">token</span><span class="p">}</span> + +<span class="n">stranka</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'https://api.github.com/user'</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">)</span> +<span class="n">stranka</span><span class="o">.</span><span class="n">raise_for_status</span><span class="p">()</span> +<span class="nb">print</span><span class="p">(</span><span class="n">stranka</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> +</pre></div><p>Co se stalo? Tím, že jsi Githubu dal/a svůj token +(načtený ze souboru, předaný přes „přihlašovací“ slovník <code>headers</code>), +poznal, že jde dotaz od tebe a vrátil nějaké informace +ve formátu JSON.</p> +<div class="admonition note"><p>Pokud kód opisuješ, pozor: v řetězci <code>'token '</code> je mezera. +„Přihlašovací“ slovník <code>headers</code> bude ve výsledku vypadat takto:</p> +<div class="highlight"><pre><span></span><span class="p">{</span><span class="s1">'Authorization'</span><span class="p">:</span> <span class="s1">'token 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'</span><span class="p">}</span> +</pre></div></div><p>Zkus řetězec <code>stranka.text</code> převést z JSON na slovník +a vypsat trochu srozumitelněji:</p> +<div class="highlight"><pre><span></span><span class="n">data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">stranka</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> + +<span class="nb">print</span><span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">ensure_ascii</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">2</span><span class="p">))</span> +</pre></div><p>Teď už je lépe vidět celý tvůj profil +(možná včetně neveřejných informací – proto musíš svůj token +udržovat v tajnosti).</p> +<p>S profilem, který máš v proměnné <code>data</code>, +se dá pracovat jako s každým jiným slovníkem. +Třeba adresu svého profilového obrázku můžeš vypsat pomocí:</p> +<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="s1">'avatar_url'</span><span class="p">])</span> +</pre></div><h2 id="api_githubu">API Githubu +<a href="#api_githubu" class="header-link">#</a> +</h2> +<p>API Githubu toho umí mnohem víc. Třeba na adrese +<a href="https://api.github.com/emojis">https://api.github.com/emojis</a> na tebe čeká +slovník s adresami malých obrázků.</p> +<!-- (Tenhle slovník funguje jako vyhledávací tabulka.) --> + +<p>Celé API je zdokumentováno na adrese +<a href="https://developer.github.com/v3/">developer.github.com</a>.</p> +<h2 id="interakce">Interakce +<a href="#interakce" class="header-link">#</a> +</h2> +<p>Pomocí webových API se dají informace nejen číst, ale i měnit.</p> +<p>Na stránce +<a href="https://github.com/pyvec/naucse.python.cz/stargazers">github.com/pyvec/naucse.python.cz/stargazers</a> +je seznam lidí, kteří „ohvězdičkovali” tyto učební materiály. +Je jich zatím málo; pojďme se k nim pomocí webového API přidat.</p> +<p>Napřed svému tokenu (na Githubu v nastavení +<a href="https://github.com/settings/tokens">Personal Accesss Tokens</a>) +přidej právo <code>public_repo</code>. +Od teď token střež obzvlášť pečlivě, protože se pomocí +něj dají informace na Githubu i měnit.</p> +<p>Chceme-li měnit informace, musíme knihovně Requests +říct, aby použila jinou „HTTP metodu” než <code>GET</code>. +Co to přesně jsou HTTP metody je na trochu delší povídání +(viz <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods">Wikipedia</a>), +ale stručně řečeno, pomocí <code>GET</code> se většinou stahuje +obsah, pomocí <code>POST</code> se přidává nový, +<code>PUT</code> mění něco, co už na webu existuje +a <code>DELETE</code> něco maže. +Jakou metodu poujeme závisí na tom, co chceme udělat; +většinou to bude <code>POST</code>, <code>PUT</code> nebo <code>DELETE</code>.</p> +<p>Podle <a href="https://docs.github.com/en/rest/reference/activity#star-a-repository-for-the-authenticated-user">dokumentace Githubu</a> +se přidání hvězdičky dělá pomocí <code>PUT</code> +dotazu na adresu <code>/user/starred/:owner/:repo</code>. +Za <code>:owner</code> a <code>:repo</code> +dosadíš vlastníka a jméno repozitáře +(v našem případě <code>pyvec</code> a <code>naucse.python.cz</code>) +a <code>PUT</code> metodu zvolíš tak, že zavoláš místo <code>get</code> funkci <code>put</code>:</p> +<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">requests</span> + +<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'token.txt'</span><span class="p">)</span> <span class="k">as</span> <span class="n">soubor</span><span class="p">:</span> + <span class="n">token</span> <span class="o">=</span> <span class="n">soubor</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> + +<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'Authorization'</span><span class="p">:</span> <span class="s1">'token '</span> <span class="o">+</span> <span class="n">token</span><span class="p">}</span> + +<span class="n">stranka</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="s1">'https://api.github.com/user/starred/pyvec/naucse.python.cz'</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">)</span> +<span class="n">stranka</span><span class="o">.</span><span class="n">raise_for_status</span><span class="p">()</span> +</pre></div><p>Tenhle dotaz nevrátí žádný text, ale na +<a href="https://github.com/pyvec/naucse.python.cz/stargazers">github.com/pyvec/naucse.python.cz/stargazers</a> +se můžeš přesvědčit, že to funguje.</p> +<p>Chceš-li hvězdičku zase odstranit, použij metodu +<code>DELETE</code> na stejnou adresu. +(Ale nezapomeň tam pak ★ zase vrátit! ☺)</p> \ No newline at end of file diff --git a/lessons/hello-world/index.html b/lessons/hello-world/index.html new file mode 100644 index 00000000..f47b8c18 --- /dev/null +++ b/lessons/hello-world/index.html @@ -0,0 +1,72 @@ +<h1 id="prvni_program">První program +<a href="#prvni_program" class="header-link">#</a> +</h1> +<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="mi">3</span> <span class="o">+</span> <span class="mi">4</span> +<span class="go">7</span> +</pre></div><p>Psaní příkazů přímo v Pythonu, <em>interaktivně</em>, +má jednu velkou nevýhodu: +to, co napíšeš, se ztratí, jakmile zavřeš okno příkazové řádky. +Na jednoduché výpočty to nevadí, ale až budou tvoje programy složitější, +budeš je potřebovat nějak uložit.</p> +<p>Otevři editor +(Ten bys měl/a mít nainstalovaný, jestli ne, instrukce jsou v <a href="naucse:page?lesson=beginners/install-editor">předchozí +lekci</a>.)</p> +<p>V něm vytvoř nový soubor, do kterého napiš následující text:</p> +<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s2">"Ahoj světe!"</span><span class="p">)</span> +</pre></div><p>Pak soubor ulož jako <code>ahoj.py</code>:</p> +<ul> +<li>V adresáři, kde máš soubory ke kurzům Pythonu, si založ adresář pojmenovaný +podle čísla lekce (např. <code>02</code>). +Měl by být vedle tvého virtuálního prostředí.</li> +<li>Do něj pak soubor ulož pod jménem <code>ahoj.py</code>.</li> +</ul> +<p>Pokud máš v ukládacím okýnku možnost zvolit <em>kódování</em>, zvol <code>UTF-8</code>. +Můžeš–li zvolit typ souboru, zvol <code>.py</code> nebo „všechny soubory“.</p> +<h2 id="spusteni">Spuštění +<a href="#spusteni" class="header-link">#</a> +</h2> +<p>Otevři si příkazovou řádku. +Pomocí <code>cd</code> donaviguj do adresáře, kde máš soubory ke kurzům Pythonu.</p> +<div class="admonition note"><p>S příkazovou řádkou jsme se seznámili +v <a href="naucse:page?lesson=beginners/cmdline">minulé lekci</a>, která popisuje i změnu aktuálního +adresáře pomocí příkazu <code>cd</code>.</p> +</div><p>Aktivuj si virtuální prostředí.</p> +<div class="admonition note"><p>Příkaz k tomu jsme si ukázali na konci +<a href="naucse:page?lesson=beginners/venv-setup">návodu na tvorbu virtuálního prostředí</a>; končí <code>activate</code>.</p> +</div><p>Pak zadej tento příkaz:</p> +<div class="highlight"><pre><span></span><span class="gp gp-VirtualEnv">(venv)</span><span class="gp">$ </span>python<span class="w"> </span>ahoj.py +</pre></div><p>Pokud se vypíše hláška <code>Ahoj světe!</code>, gratuluji! +Napsal/a jsi svůj první program v Pythonu!</p> +<p>Jestli to nefunguje, zkontroluj, že:</p> +<ul> +<li>Máš zapnuté virtuální prostředí. +(Na příkazové řádce se musí ukazovat <code>(venv)</code>; +pokud tam není, použij příkaz „activate“ z <a href="naucse:page?lesson=beginners/install">minula</a>.)</li> +<li>Jsi ve správném adresáři. Zkus <code>pwd</code> (Unix) nebo <code>cd</code> (Windows). +Aktuální adresář musí být ten, do kterého jsi uložil/a +soubor s programem.</li> +<li>Soubor se opravdu jmenuje <code>ahoj.py</code>. +Pomocí <code>ls</code> (Unix) nebo <code>dir</code> (Windows) zkontroluj, že se soubor opravdu +jmenuje <code>ahoj.py</code> a ne třeba <code>ahoj.py.txt</code>. +Jestli ne, ulož ho znovu pod správným jménem.</li> +<li>Soubor <code>ahoj.py</code> obsahuje správný příkaz, včetně všech uvozovek a závorek.</li> +<li>Slovo <code>(venv)</code> ani znak <code>$</code> nezadáváš – v materiálech jsou proto, aby bylo +poznat, že jde o příkaz příkazové řádky. +Na <code>$</code> (nebo, na Windows, <code>></code>) končí dotaz, který vypíše sám počítač. +Příkaz, který zadáváš ty, je jen <code>python ahoj.py</code>.</li> +</ul> +<p>A jestli to pořád nefunguje, zeptej se +zkušenějšího programátora. <!-- XXX: where to direct people? --></p> +<div class="admonition style-note"><p class="admonition-title">Typografická vsuvka</p> +<p>V Pythonu je většinou jedno, kde napíšeš mezeru. Stejně jako náš příkaz +<code>print("Ahoj světe!")</code> by fungovalo třeba:</p> +<div class="highlight"><pre><span></span><span class="nb">print</span> <span class="p">(</span> <span class="s2">"Ahoj světe!"</span> <span class="p">)</span> +</pre></div><p>Je ale zvykem dodržovat určitá pravidla. +Jako v češtině se po otvírací závorce a za +ozavírací závorkou nepíše mezera. +Na rozdíl od češtiny ale mezeru nepiš ani mezi <code>print</code> a závorkou. +„Správně“ je tedy:</p> +<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s2">"Ahoj světe!"</span><span class="p">)</span> +</pre></div><p>V rámci uvozovek má pak každá mezera význam: když napíšeš +<code>" Ahoj světe!"</code>, mezery navíc se objeví ve výsledné hlášce.</p> +</div> \ No newline at end of file diff --git a/lessons/http/index.html b/lessons/http/index.html new file mode 100644 index 00000000..92a6e05b --- /dev/null +++ b/lessons/http/index.html @@ -0,0 +1,141 @@ +<h1 id="http_jak_funguje_internet">HTTP – Jak funguje Internet +<a href="#http_jak_funguje_internet" class="header-link">#</a> +</h1> +<p>Než začneme pracovat s internetem – ať už tvorbou vlastních stránek, nebo +komunikací s existujícími službami, pojďme si přiblížit, co vlastně ten +internet je a jak funguje.</p> +<p>Internet je celosvětová síť počítačů. +Je to spousta laptopů, stolních počítačů, malých blikajících krabiček +i obrovských blikajících skříní, které jsou navzájem propojeny pomocí +kabelů (nebo i bezdrátově).</p> +<p>Samozřejmě není každé zařízení propojené s každým jiným zařízením – tolik +kabelů by se na Zemi těžko vešlo. +Spousta zařízení – hlavně tzv. <em>routery</em> a <em>switche</em> – ale umí přeposílat +zprávy mezi sebou tak, že každý počítač může komunikovat s každým +jiným počítačem. +(Aspoň teoreticky – reálně je komunikace omezená např. kvůli bezpečnosti.)</p> +<p>Funguje to podobně jako pošta: když pošlu balíček z Brna do Melbourne, +nedostane se tam přímo. +Balíček poputuje třeba vlakem do Prahy, pak letadlem do hlavní pošty +v Austrálii a odtud náklaďákem do Melbourne, kde ho doručovatel donese až +k domu příjemce. +A k naplánování celé téhle cesty stačí napsat na obálku krátkou adresu.</p> +<p>Podobně cestují informace v internetu: z laptopu přes Wi-Fi do <em>routeru</em>, +odtud kabelem k poskytovateli připojení, tlustším kabelem do české +„páteřní sítě“, podmořským kabelem třeba do Ameriky… a nakonec k počítači, +se kterým jsem chtěl komunikovat.</p> +<p>Většinou můj laptop takhle komunikuje se <em>serverem</em>, počítačem, který +se stará o sdělování informací. +Každou webovou stránku spravuje takový server.</p> +<p><a id="url-anatomy"></a></p> +<h2 id="webove_adresy">Webové adresy +<a href="#webove_adresy" class="header-link">#</a> +</h2> +<p>Jak taková komunikace vypadá si ukážeme na příkladu – +co se stane, když do prohlížeče zadám tuhle adresu:</p> +<div class="highlight"><pre><code>http://naucse.python.cz/lessons/fast-track/http/ +</code></pre></div><p>Taková webová adresa – technicky zvaná URL (<em>Uniform Resource Locator</em>, +„jednotná adresa zdroje“) přesně určuje, jak se má prohlížeč dostat +k informacím, které má zobrazit.</p> +<p><span class="figure"><a href="naucse:static?filename=url-anatomy.svg"><img src="naucse:static?filename=url-anatomy.svg" alt="http://naucse.python.cz/lessons/fast-track/http/"></a></span></p> +<p>Začátek adresy, <code>http://</code>, je jméno <em>protokolu</em> (angl. <em>protocol name</em>). +Protokol určuje způsob, <em>jak</em> se k daným informacím dostat. +Protokolů existuje spousta, každý funguje trochu jinak a každý se používá +na něco jiného: +SMTP a POP pro e-mail, FTP pro přenos souborů, SSH pro ovládání počítačů. +My se teď ale zaměříme na HTTP, který se typicky používá pro webové stránky.</p> +<p>Další část adresy, <code>naucse.python.cz</code>, je <em>jméno serveru</em> (angl. <em>server name</em>). +Říká, <em>kde</em> prohlížeč najde dané informace.</p> +<p>Jméno serveru je jako poštovní adresa – existuje počítač, který se jmenuje +<code>naucse.python.cz</code>, a každý internetový „pošťák“ ví, komu přeposlat zprávu, +aby se k tomuto počítači nakonec dostala.</p> +<div class="admonition note"><p>„Skutečná“ adresa počítače, tzv. IP adresa, je číselná – například +<code>151.101.37.147</code> nebo <code>2a04:4e42:9::403</code>. +Existuje ale systém, jak jméno serveru na takovou <em>IP adresu</em> přeložit. +Tenhle systém se jmenuje DNS a – abychom zůstali u přirovnání k poště – +funguje podobně jako seznamy poštovních směrovacích čísel.</p> +</div><p>Poslední část URL, <code>/lessons/fast-track/http/</code>, je <em>cesta</em> (angl. <em>path</em>). +Říká, <em>co</em> chceme od serveru dostat: jméno konkrétní webové stránky.</p> +<p>U jednodušších stránek to může být přímo jméno souboru, který má server +uložený na disku – proto spousta adres na Webu končí příponou <code>.html</code>.</p> +<h2 id="pozadavek_a_odpoved">Požadavek a odpověď +<a href="#pozadavek_a_odpoved" class="header-link">#</a> +</h2> +<p>K získání požadované stránky prohlížeč vytvoří <em>požadavek</em> (angl. <em>request</em>) +– zprávu „Pošli mi prosím stránku <code>/lessons/fast-track/http/</code>“ – a pošle ho +serveru <code>naucse.python.cz</code>.</p> +<p>Server požadavek dostane a vrátí <em>odpověď</em> (angl. <em>response</em>) – zprávu +s obsahem dané stránky. +Obsah je často webová stránka v jazyce HTML, který popisuje co na stránce je, +kde jsou nadpisy a kde odstavce, jak má stránka vypadat, a tak dále. +Ale v odpovědi může být místo stránky i cokoli jiného – obrázek, video, nebo +jiná data.</p> +<p>Veškerá komunikace přes HTTP funguje právě takto: pošle se požadavek +a přijde na něj odpověď.</p> +<p>A jak tyhle zprávy vypadají? +Požadavek nějak takhle:</p> +<div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/lessons/fast-track/install/</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> +<span class="na">Accept</span><span class="o">:</span> <span class="l">*/*</span> +<span class="na">Accept-Encoding</span><span class="o">:</span> <span class="l">gzip, deflate</span> +<span class="na">Connection</span><span class="o">:</span> <span class="l">keep-alive</span> +<span class="na">Host</span><span class="o">:</span> <span class="l">naucse.python.cz</span> +<span class="na">User-Agent</span><span class="o">:</span> <span class="l">Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0</span> +</pre></div><p>První řádek říká serveru: prosím, pošli mi (<code>GET</code>) stránku +<code>/lessons/fast-track/install/</code> s použitím protokolu <code>HTTP</code> verze <code>1.1</code>. +Další řádky jsou <em>hlavičky</em> (angl. <em>headers</em>). +Říkají například kdo se ptá (<code>User-Agent</code>) a jaký obsah očekává (<code>Accept</code>). +Většina hlaviček je nepovinná.</p> +<p>Odpověď pak může vypadat takto:</p> +<div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span> +<span class="na">Cache-Control</span><span class="o">:</span> <span class="l">max-age=600</span> +<span class="na">Connection</span><span class="o">:</span> <span class="l">keep-alive</span> +<span class="na">Content-Encoding</span><span class="o">:</span> <span class="l">gzip</span> +<span class="na">Content-Length</span><span class="o">:</span> <span class="l">3127</span> +<span class="na">Content-Type</span><span class="o">:</span> <span class="l">text/html; charset=utf-8</span> +<span class="na">Date</span><span class="o">:</span> <span class="l">Tue, 20 Feb 2018 15:51:24 GMT</span> +<span class="na">Last-Modified</span><span class="o">:</span> <span class="l">Tue, 20 Feb 2018 15:20:08 GMT</span> +<span class="na">Server</span><span class="o">:</span> <span class="l">GitHub.com</span> + +<span class="cp"><!doctype html></span> + <span class="p"><</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">"cs"</span><span class="p">></span> + <span class="p"><</span><span class="nt">head</span><span class="p">></span> + <span class="p"><</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">></span> + <span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"viewport"</span> <span class="na">content</span><span class="o">=</span><span class="s">"width=device-width, initial-scale=1, shrink-to-fit=no"</span><span class="p">></span> + <span class="p"><</span><span class="nt">title</span><span class="p">></span> + Python a jeho knihovny: HTTP – Jak funguje Internet +… +</pre></div><p>První řádek říká: používáme protokol <code>HTTP</code> verze <code>1.1</code>, +a všechno je v pořádku (<code>200 OK</code>). +Kromě <code>200</code> existují i další <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes">stavové kódy</a> (angl. <em>status codes</em>). +Známý je např. <code>404</code> „nenalezeno“.</p> +<p>Následují opět hlavičky – např. kdo odpovídá (<code>Server</code>), kdy byla stránka +naposledy změněna (<code>Last-Modified</code>) a jak je odpověď zakódovaná: +<code>Content-Type: text/html</code> říká, že je to stránka v jazyce HTML.</p> +<p>Hlavičky jsou ukončené volným řádkem, po kterém následuje samotný obsah +odpovědi ve zmíněném jazyce HTML.</p> +<h2 id="http_metody">HTTP Metody +<a href="#http_metody" class="header-link">#</a> +</h2> +<p>Komunikace ukázaná výše používala metodu <code>GET</code>, která slouží ke <em>čtení</em> +informací. +Když se takto prohlížeč na nějakou stránku zeptá, nic se na serveru nezmění. +Prohlížeč si takovou stránku – nebo třeba obrázek či video – může dočasně +uložit, a když bude potřeba znovu, použít uloženou verzi.</p> +<p>Některými požadavky ale stav serveru mění: například se přihlásí uživatel, +nakoupí zboží v e-shopu nebo odešle zpráva do diskuse. +Tyto požadavky používají místo <code>GET</code> jinou <em>metodu</em> (angl. <em>method</em>). +Co přesně která metoda na jaké adrese dělá, to záleží na autorovi stránek. +Často se používají tyto metody:</p> +<ul> +<li><code>GET</code> načte informace,</li> +<li><code>POST</code> pošle na server informace, např. z formuláře, s cílem něco +změnit nebo nastavit,</li> +<li><code>PUT</code> přidá novou stránku (nebo jiný objekt),</li> +<li><code>DELETE</code> něco smaže.</li> +</ul> +<p>Seznam všech metod je ve +<a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">specifikaci</a>.</p> +<p>U složitějších požadavků se dají na server poslat i informace: +webové formuláře se odesílají požadavkem, který používá metodu <code>POST</code> +a vyplněné informace k dotazu „přilepí“ za hlavičky – stejným způsobem, jako se +v odpovědi posílá HTML stránka.</p> \ No newline at end of file diff --git a/lessons/fast-track/http/static/url-anatomy.svg b/lessons/http/url-anatomy.svg similarity index 100% rename from lessons/fast-track/http/static/url-anatomy.svg rename to lessons/http/url-anatomy.svg diff --git a/lessons/ignoring/index.html b/lessons/ignoring/index.html new file mode 100644 index 00000000..3fc7fc97 --- /dev/null +++ b/lessons/ignoring/index.html @@ -0,0 +1,233 @@ +<h1 id="ignorovani_souboru">Ignorování souborů +<a href="#ignorovani_souboru" class="header-link">#</a> +</h1> +<p>Často se stává, že některé soubory v repozitáři nechceš. +Takových souborů jsou tři hlavní druhy:</p> +<dl> +<dt>Pomocné soubory nástrojů</dt> +<dd>Python občas „sám od sebe“ vytváří adresář <code>__pycache__</code> s pomocnými +soubory, aby zrychlil importování modulů. +Některé počítače vytváří skryté soubory s názvy jako +<code>.Thumbnails</code>, <code>.DS_Store</code> nebo <code>Thumbs.db</code>. +Takové věci v repozitáři nemají co dělat – je +dobrým zvykem do Gitu nedávat nic, co jde vytvořit automaticky.</dd> +<dt>Výstup programu a nastavení</dt> +<dd>Píšeš-li program, který kreslí obrázky, většinou chceš v repozitáři +jen samotný program. +Obrázky si může pomocí programu každý vytvořit sám.</dd> +<dd>Podobně fungují soubory s heslem: pokud program potřebuje heslo +např. k nějaké webové službě, ale svoje heslo nechceš dávat veřejně +k dispozici, musí si každý vytvořit soubor s heslem sám.</dd> +<dt>Osobní soubory</dt> +<dd>Občas se stane, že v adresáři s repozitářem máš soubor s osobními +poznámkami. +Zbytek repozitáře plánuješ zveřejnit, ale tyto soubory by měly zůstat +jen ve tvé kopii. A to včetně informace o tom, že takové soubory máš.</dd> +<dt>Adresář s virtuálním prostředím</dt> +<dd>Jistě už sis zvykl/a na virtuální prostředí. +Adresář s ním se může jmenovat různě, v začátečnickém kurzu používáme název <code>venv</code>. +Není dobré tento adresář dávat do Gitu, +protože je jednoduché jej vždy vytvořit znovu +a pokud na projektu spolupracuje více lidí +(nebo ty z více počítačů), mohlo by dělat neplechu, kdyby virtuální +prostředí nebylo vždy na úplně stejném místě. +Virtuální prostředí z adresáře <code>/home/helena/projektABC/venv</code> +nebude fungovat z adresáře <code>C:\Users\Helena\projektABC\venv</code>, +ale ani z <code>/home/mirka/projektABC/venv</code>.</dd> +</dl> +<p>My budeme chtít Git nastavit tak, aby tyto soubory ignoroval: aby +<code>git status</code> neukazoval červeně, že ještě nejsou v repozitáři.</p> +<h2 id="priprava">Příprava +<a href="#priprava" class="header-link">#</a> +</h2> +<p>Pojďme si to ukázat na příkladu. +Založ si nový repozitář a vytvoř v něm tři soubory s tímto obsahem:</p> +<ul> +<li><p><code>obrazek.py</code></p> +<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">turtle</span> <span class="kn">import</span> <span class="n">forward</span><span class="p">,</span> <span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">,</span> <span class="n">getcanvas</span> + +<span class="n">forward</span><span class="p">(</span><span class="mi">50</span><span class="p">)</span> +<span class="n">left</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span> +<span class="n">forward</span><span class="p">(</span><span class="mi">50</span><span class="p">)</span> +<span class="n">right</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span> +<span class="n">forward</span><span class="p">(</span><span class="mi">50</span><span class="p">)</span> + +<span class="n">getcanvas</span><span class="p">()</span><span class="o">.</span><span class="n">postscript</span><span class="p">(</span><span class="n">file</span><span class="o">=</span><span class="s1">'obrazek.ps'</span><span class="p">)</span> +</pre></div></li> +<li><p><code>poznamky.txt</code></p> +<div class="highlight"><pre><code>Tohle je tajné! +</code></pre></div></li> +<li><p><code>Autofile.tmp</code></p> +<p>Do tohohle souboru napiš cokoliv. +Různé operační systémy a (jiné programy) vytváří různé soubory +podivných jmen; <code>Autofile.tmp</code> pro nás bude představovat takový +automaticky vzniklý soubor.</p> +</li> +</ul> +<p>Pythonní program spusť (pomocí <code>python obrazek.py</code>). +Mělo by se na chvíli ukázat okno s želvou a měl by vzniknout nový soubor +<code>obrazek.ps</code>.</p> +<div class="admonition note"><p>Obrázek ve formátu PostScript (.ps) se dá otevřít ve většině programů, které +zvládají i PDF, případně v <a href="https://inkscape.org/">Inkscape</a>.</p> +</div><p>Jak se na to dívá Git?</p> +<div class="highlight"><pre><code><span style="color: #00aaaa">$</span> git status +On branch master + +No commits yet + +Untracked files: + (use "git add <file>..." to include in what will be committed) + <span style="color: #aa0000">Autofile.tmp</span> + <span style="color: #aa0000">obrazek.ps</span> + <span style="color: #aa0000">obrazek.py</span> + <span style="color: #aa0000">poznamky.txt</span> + +nothing added to commit but untracked files present (use "git add" to track) +</code></pre></div><p>Spousta souborů, ale jen jeden z nich chceš v repozitáři. +Co s těmi ostatními?</p> +<h2 id="vystupy_programu_a_pomocne_soubory_spolecnych_nastroju">Výstupy programu a pomocné soubory společných nástrojů +<a href="#vystupy_programu_a_pomocne_soubory_spolecnych_nastroju" class="header-link">#</a> +</h2> +<p>Nejjednodušší je vyrovnat se se souborem <code>obrazek.ps</code>. +V repozitáři ho nechceš (je to repozitář <em>zdrojového</em> kódu; výsledky bývá +lepší schraňovat jinde než v Gitu). +Zároveň víš, že každý, kdo s repozitářem bude pracovat, pravděpodobně +tenhle soubor vytvoří. +Bylo by tedy dobré říct <em>všem</em> lidem, kteří se k repozitáři dostanou, že tento +soubor do Gitu nepatří. +To se dělá záznamem ve speciálním souboru v repozitáři.</p> +<p>Udělej soubor s názvem <code>.gitignore</code>. +(Pozor na tečku ve jménu souboru; na některých systémech se špatně zadává – +doporučuji soubor vytvořit v programátorském editoru.) +Do něj napiš:</p> +<div class="highlight"><pre><code>obrazek.ps +</code></pre></div><p>Pak se podívej na <code>git status</code>. Obrázek už by ve výpisu neměl být!</p> +<div class="highlight"><pre><code><span style="color: #00aaaa">$</span> git status +On branch master + +No commits yet + +Untracked files: + (use "git add <file>..." to include in what will be committed) + <span style="color: #aa0000">.gitignore</span> + <span style="color: #aa0000">Autofile.tmp</span> + <span style="color: #aa0000">obrazek.py</span> + <span style="color: #aa0000">poznamky.txt</span> + +nothing added to commit but untracked files present (use "git add" to track) +</code></pre></div><p>Nový soubor <code>.gitignore</code> přidej do repozitáře společně se samotným programem:</p> +<div class="highlight"><pre><code><span style="color: #00aaaa">$</span> git add .gitignore obrazek.py +<span style="color: #00aaaa">$</span> git status +On branch master + +No commits yet + +Changes to be committed: + (use "git rm --cached <file>..." to unstage) + <span style="color: #00aa00">new file: .gitignore</span> + <span style="color: #00aa00">new file: obrazek.py</span> + +Untracked files: + (use "git add <file>..." to include in what will be committed) + <span style="color: #aa0000">Autofile.tmp</span> + <span style="color: #aa0000">poznamky.txt</span> +</code></pre></div><p>Když uděláš <code>git commit</code> a repozitář nasdílíš s ostatními, všichni dostanou +<code>.gitignore</code> s instrukcí, že <code>obrazek.ps</code> do repozitáře nepatří.</p> +<p>Jak ignorovat zbylé dva soubory?</p> +<h2 id="osobni_poznamky">Osobní poznámky +<a href="#osobni_poznamky" class="header-link">#</a> +</h2> +<p>Soubor <code>poznamky.txt</code> se taky dá zařadit do <code>.gitignore</code>, ale moc se tam nehodí. +Existuje jen u tebe; není důvod předpokládat, že si někdo jiný vytvoří +soubor se stejným jménem.</p> +<p>Dej ho tedy do souboru, který se nebude šířit s repozitářem. +Tento soubor je <code>.git/info/exclude</code>. +(Může být trochu složité ho najít, protože adresář <code>.git</code> je skrytý. +Nevidíš–li ho, napiš v editoru do okýnka pro otevření souboru <code>.git</code> a dostaneš +se do něj.)</p> +<p>Soubory v adresáři <code>.git</code> bys neměl/a měnit, protože se tak dá přijít +o historii projektu. +Ale <code>exclude</code> je výjimka. Napiš na konec tohoto souboru:</p> +<div class="highlight"><pre><code>poznamky.txt +</code></pre></div><p>A po uložení budou poznámky ignorovány!</p> +<div class="highlight"><pre><code><span style="color: #00aaaa">$</span> git status +On branch master + +No commits yet + +Changes to be committed: + (use "git rm --cached <file>..." to unstage) + <span style="color: #00aa00">new file: .gitignore</span> + <span style="color: #00aa00">new file: obrazek.py</span> + +Untracked files: + (use "git add <file>..." to include in what will be committed) + <span style="color: #aa0000">Autofile.tmp</span> +</code></pre></div><h2 id="dalsi_haraburdi">Další haraburdí +<a href="#dalsi_haraburdi" class="header-link">#</a> +</h2> +<p>Některé programy automaticky vytvářejí pomocné soubory. +Často to dělají správci souborů (často <code>.Thumbnails</code> na Linuxu, +<code>.DS_Store</code> na Macu nebo <code>Thumbs.db</code> na Windows). +Některé editory si taky nechávají na disku nastavení.</p> +<p>Podobné soubory se dají dát do <code>.gitignore</code>. +Je ale lepší si je dát do osobního nastavení, protože ostatní lidé, +kteří na projektu můžou spolupracovat, nemusí používat stejný systém +a programy.</p> +<div class="admonition note"><p>Pokud si můžeš být jistý/á, že ostatní budou používat právě +ten program, který používáš ty, použij <code>.gitignore</code>. +Příklad je adresář <code>__pycache__</code>, který vytváří Python při importu modulu.</p> +</div><p>Soubor s osobním nastavením si můžeš pojmenovat, jak chceš, a můžeš ho uložit +kde budeš chtít. +Já doporučuji ho pojmenovat <code>.gitignore_global</code> a dát ho do tvého domovského +adresáře.</p> +<p>Do souboru zase napiš jméno ignorovaného souboru:</p> +<div class="highlight"><pre><code>Autofile.tmp +</code></pre></div><p>Potom řekni Gitu, kde tento soubor najít:</p> +<div class="highlight"><pre><code><span style="color: #00aaaa">$</span> git config --global core.excludesfile /tmp/tmp.1spGPvBL5W/.gitignore_global +</code></pre></div><p>A měl by být ignorován:</p> +<div class="highlight"><pre><code><span style="color: #00aaaa">$</span> git status +On branch master + +No commits yet + +Changes to be committed: + (use "git rm --cached <file>..." to unstage) + <span style="color: #00aa00">new file: .gitignore</span> + <span style="color: #00aa00">new file: obrazek.py</span> +</code></pre></div><h2 id="format_ignorovaciho_souboru">Formát ignorovacího souboru +<a href="#format_ignorovaciho_souboru" class="header-link">#</a> +</h2> +<p>Ve všech třech „ignorovacích“ souborech lze samozřejmě uvést víc souborů: +každý na zvláštní řádek. +Kromě toho je možné použít několik vychytávek:</p> +<ul> +<li><p><code>*</code> ve jméně souboru nahradí část jména souboru. +Takže pokud chceš ignorovat všechny soubory s příponou <code>.tmp</code>, můžeš napsat:</p> +<div class="highlight"><pre><code>*.tmp +</code></pre></div></li> +<li><p><code>/</code> na konci jména značí adresář. Chceš-li tedy ignorovat adresáře +<code>__pycache__</code> (což v Pythonním projektu chceš), napiš do <code>.gitignore</code>:</p> +<div class="highlight"><pre><code>__pycache__/ +</code></pre></div></li> +</ul> +<p>Další detaily je možné najít v <a href="https://git-scm.com/docs/gitignore">dokumentaci</a>.</p> +<h2 id="automaticke_pridavani">Automatické přidávání +<a href="#automaticke_pridavani" class="header-link">#</a> +</h2> +<p>Teď, když umíš ignorovat soubory, si můžeme ukázat zkratku. Místo</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>add<span class="w"> </span>soubor1<span class="w"> </span>soubor2 +<span class="gp">$ </span>git<span class="w"> </span>commit +</pre></div><p>můžeš napsat jen <code>git commit</code> s tečkou na konci:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>commit<span class="w"> </span>. +</pre></div><p>To automaticky přidá <em>všechny</em> neignorované soubory, které <code>git status</code> ukazuje +červeně. +Tečka je jméno pro aktuální adresář – celý adresář a všechno pod ním se přidá +do revize.</p> +<p>Doporučuji si před použitím téhle zkratky zkontrolovat <code>git status</code>, aby sis +ověřil/a, že nepřidáváš nic, co nechceš.</p> +<p>Taky doporučuji si nastavit Git, aby se v editoru, kam píšeš popisek revize, +ukazovala poznámka s tím, co vlastně v nové revizi bude. +Uvidíš tak něco jako <code>git status</code> vždy, když začneš psát popisek k revizi:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>config<span class="w"> </span>--global<span class="w"> </span>commit.verbose<span class="w"> </span><span class="m">1</span> +</pre></div> \ No newline at end of file diff --git a/lessons/inheritance/index.html b/lessons/inheritance/index.html new file mode 100644 index 00000000..4c30ae40 --- /dev/null +++ b/lessons/inheritance/index.html @@ -0,0 +1,207 @@ +<h1 id="dedicnost">Dědičnost +<a href="#dedicnost" class="header-link">#</a> +</h1> +<p>Minule jsme probrali třídy. +Jako příklad jsme si ukázali třídu pro koťátka:</p> +<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Kotatko</span><span class="p">:</span> + <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jmeno</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span> <span class="o">=</span> <span class="n">jmeno</span> + + <span class="k">def</span> <span class="nf">snez</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jidlo</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">jidlo</span><span class="si">}</span><span class="s2"> mi chutná!"</span><span class="p">)</span> + + <span class="k">def</span> <span class="nf">zamnoukej</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: Mňau!"</span><span class="p">)</span> +</pre></div><p>Zkus si udělat podobnou třídu pro štěňátka:</p> +<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Stenatko</span><span class="p">:</span> + <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jmeno</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span> <span class="o">=</span> <span class="n">jmeno</span> + + <span class="k">def</span> <span class="nf">snez</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jidlo</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">jidlo</span><span class="si">}</span><span class="s2"> mi chutná!"</span><span class="p">)</span> + + <span class="k">def</span> <span class="nf">zastekej</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: Haf!"</span><span class="p">)</span> +</pre></div><p>Většina kódu je stejná! +Kdybys měl/a napsat i třídu pro kuřátka, kůzlátka, +slůňátka a háďátka, bez <kbd>Ctrl</kbd>+<kbd>C</kbd> by to bylo docela nudné. +A protože jsou programátoři líní psát stejný kód +několikrát (a hlavně ho potom udržovat), vymysleli +mechanismus, jak se toho vyvarovat.</p> +<p>Koťátka i štěňátka jsou zvířátka. +Můžeš si vytvořit třídu společnou pro všechna +zvířátka a do ní napsat všechno, co je společné. +Ve třídách pro jednotlivé druhy zvířat pak zbude jen to, co se liší. +V Pythonu se to dělá takto:</p> +<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Zviratko</span><span class="p">:</span> + <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jmeno</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span> <span class="o">=</span> <span class="n">jmeno</span> + + <span class="k">def</span> <span class="nf">snez</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jidlo</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">jidlo</span><span class="si">}</span><span class="s2"> mi chutná!"</span><span class="p">)</span> + + +<span class="k">class</span> <span class="nc">Kotatko</span><span class="p">(</span><span class="n">Zviratko</span><span class="p">):</span> + <span class="k">def</span> <span class="nf">zamnoukej</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: Mňau!"</span><span class="p">)</span> + + +<span class="k">class</span> <span class="nc">Stenatko</span><span class="p">(</span><span class="n">Zviratko</span><span class="p">):</span> + <span class="k">def</span> <span class="nf">zastekej</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: Haf!"</span><span class="p">)</span> + + +<span class="n">micka</span> <span class="o">=</span> <span class="n">Kotatko</span><span class="p">(</span><span class="s1">'Micka'</span><span class="p">)</span> +<span class="n">azorek</span> <span class="o">=</span> <span class="n">Stenatko</span><span class="p">(</span><span class="s1">'Azorek'</span><span class="p">)</span> +<span class="n">micka</span><span class="o">.</span><span class="n">zamnoukej</span><span class="p">()</span> +<span class="n">azorek</span><span class="o">.</span><span class="n">zastekej</span><span class="p">()</span> +<span class="n">micka</span><span class="o">.</span><span class="n">snez</span><span class="p">(</span><span class="s1">'myš'</span><span class="p">)</span> +<span class="n">azorek</span><span class="o">.</span><span class="n">snez</span><span class="p">(</span><span class="s1">'kost'</span><span class="p">)</span> +</pre></div><p>Jak to funguje? +Příkazem <code>class Kotatko(Zviratko):</code> +říkáš Pythonu, že třída <code>Kotatko</code> +<em>dědí</em> ze třídy <code>Zviratko</code> +(angl. <em>inherits</em> from <code>Zviratko</code>). +Případně se můžeš setkat s jinými termíny: +„je odvozená” ze třídy <code>Zviratko</code>, +(angl. <em>derived from</em>), +nebo ji “rozšiřuje” (angl. <em>extends</em>). +A když už jsme u terminologie, odvozeným třídám se +říká taky <em>podtřídy</em> (angl. <em>subclasses</em>) +a <code>Zviratko</code> je tu <em>nadtřída</em> +(angl. <em>superclass</em>).</p> +<p>Když potom Python hledá nějakou metodu +(nebo jiný atribut), třeba <code>micka.snez</code>, +a nenajde ji přímo ve třídě daného objektu (u nás +<code>Kotatko</code>), podívá se do nadtřídy. +Takže všechno, co je definované pro +<code>Zviratko</code>, platí i pro koťátka – dokud to výslovně nezměníš.</p> +<h2 id="prepisovani_metod_a_codesupercode">Přepisování metod a <code>super()</code> +<a href="#prepisovani_metod_a_codesupercode" class="header-link">#</a> +</h2> +<p>Když se ti nebude líbit chování některé metody +v nadtřídě, stačí dát metodu stejného jména do +podtřídy:</p> +<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Kotatko</span><span class="p">(</span><span class="n">Zviratko</span><span class="p">):</span> + <span class="k">def</span> <span class="nf">snez</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jidlo</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">jidlo</span><span class="si">}</span><span class="s2"> mi vůbec nechutná!"</span><span class="p">)</span> + + +<span class="n">micka</span> <span class="o">=</span> <span class="n">Kotatko</span><span class="p">(</span><span class="s1">'Micka'</span><span class="p">)</span> +<span class="n">micka</span><span class="o">.</span><span class="n">snez</span><span class="p">(</span><span class="s1">'granule'</span><span class="p">)</span> +</pre></div><div class="admonition note"><p>Je to podobné jako když jsme minule přepisovali +atribut pomocí <code>micka.zamnoukej = 12345</code>. +Python atributy hledá napřed na samotném objektu, +potom na třídě toho objektu a pak na nadtřídě +(a případně dalších nadtřídách té nadtřídy).</p> +</div><p>Občas se může stát, že v takovéto přepsané metodě budeš +potřebovat použít původní funkčnost, jen budeš chtít udělat ještě něco navíc. +To umí zařídit speciální funkce <code>super()</code>, +která umožňuje volat metody z nadtřídy. +Třeba takhle:</p> +<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Kotatko</span><span class="p">(</span><span class="n">Zviratko</span><span class="p">):</span> + <span class="k">def</span> <span class="nf">snez</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jidlo</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"(</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2"> na </span><span class="si">{</span><span class="n">jidlo</span><span class="si">}</span><span class="s2"> chvíli fascinovaně kouká)"</span><span class="p">)</span> + <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">snez</span><span class="p">(</span><span class="n">jidlo</span><span class="p">)</span> + + +<span class="n">micka</span> <span class="o">=</span> <span class="n">Kotatko</span><span class="p">(</span><span class="s1">'Micka'</span><span class="p">)</span> +<span class="n">micka</span><span class="o">.</span><span class="n">snez</span><span class="p">(</span><span class="s1">'granule'</span><span class="p">)</span> +</pre></div><p>Pozor na to, že takhle volané metodě musíš dát všechny +argumenty, které potřebuje (kromě <code>self</code>, +který se jako obvykle doplní automaticky). +Toho se dá i využít – můžeš použít i jiné argumenty +než dostala původní funkce:</p> +<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Hadatko</span><span class="p">(</span><span class="n">Zviratko</span><span class="p">):</span> + <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jmeno</span><span class="p">):</span> + <span class="n">jmeno</span> <span class="o">=</span> <span class="n">jmeno</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">'s'</span><span class="p">,</span> <span class="s1">'sss'</span><span class="p">)</span> + <span class="n">jmeno</span> <span class="o">=</span> <span class="n">jmeno</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">'S'</span><span class="p">,</span> <span class="s1">'Sss'</span><span class="p">)</span> + <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">jmeno</span><span class="p">)</span> + + +<span class="n">standa</span> <span class="o">=</span> <span class="n">Hadatko</span><span class="p">(</span><span class="s1">'Stanislav'</span><span class="p">)</span> +<span class="n">standa</span><span class="o">.</span><span class="n">snez</span><span class="p">(</span><span class="s1">'myš'</span><span class="p">)</span> +</pre></div><p>Jak je vidět, <code>super()</code> se dá bez problémů +kombinovat se speciálními metodami jako <code>__init__</code>. +Dokonce se to dělá poměrně často!</p> +<h2 id="polymorfismus">Polymorfismus +<a href="#polymorfismus" class="header-link">#</a> +</h2> +<p>Programátoři nezavedli dědičnost jen proto, že jsou +líní a nechtějí psát dvakrát stejný kód. +To je sice dobrý důvod, ale nadtřídy mají jednu +důležitější vlastnost. Když víš, že každé +<code>Kotatko</code> nebo <code>Stenatko</code> +nebo jakákoli jiná podtřída je zvířátko, +můžeš si udělat seznam zvířátek s tím, +že pak bude jedno, jaká přesně zvířátka to jsou:</p> +<div class="highlight"><pre><span></span><span class="n">zviratka</span> <span class="o">=</span> <span class="p">[</span><span class="n">Kotatko</span><span class="p">(</span><span class="s1">'Micka'</span><span class="p">),</span> <span class="n">Stenatko</span><span class="p">(</span><span class="s1">'Azorek'</span><span class="p">)]</span> + +<span class="k">for</span> <span class="n">zviratko</span> <span class="ow">in</span> <span class="n">zviratka</span><span class="p">:</span> + <span class="n">zviratko</span><span class="o">.</span><span class="n">snez</span><span class="p">(</span><span class="s1">'flákota'</span><span class="p">)</span> +</pre></div><p>Tohle je docela důležitá vlastnost podtříd: +když máš nějaké <code>Kotatko</code>, můžeš ho použít +kdekoliv kde program očekává <code>Zviratko</code>, +protože každé koťátko <em>je</em> zvířátko.</p> +<div class="admonition note"><p>Tohle je docela dobrá pomůcka pro případy, kdy nebudeš vědět, +kterou třídu podědit z které. +Každé <em>koťátko</em> nebo <em>štěňátko</em> +je <em>zvířátko</em>, každá <em>chata</em> +nebo <em>panelák</em> je <em>stavení</em>. +V takových případech dává dědičnost smysl.</p> +<p>Někdy se ale stane, že tuhle pomůcku zkusíš použít a vyjde ti +nesmysl jako „každé auto je volant”. +V takovém případě dědičnost nepoužívej. +I když jak auto tak volant se dají „otočit doprava”, +u každého to znamená něco jiného – a určitě nejde auto +použít kdekoli, kde bys chtěl/a použít volant. +Takže v tomto případě je lepší si říct „každé auto +<em>má</em> volant”, stejně jako „každé kotě +<em>má</em> jméno”, udělat dvě nezávislé třídy a napsat něco jako:</p> +<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Auto</span><span class="p">:</span> + <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">volant</span> <span class="o">=</span> <span class="n">Volant</span><span class="p">()</span> +</pre></div><p>(A až bude někdy nějaký vystudovaný informatik nespokojený +s tím, že porušuješ +<a href="https://en.wikipedia.org/wiki/Liskov_substitution_principle">Liskovové substituční princip</a>, +jde o právě tento problém.)</p> +</div><h2 id="generalizace">Generalizace +<a href="#generalizace" class="header-link">#</a> +</h2> +<p>Když se teď podíváš na funkce <code>zamnoukej</code> +a <code>zastekej</code>, možná tě napadne, že by se +daly pojmenovat lépe, aby se daly použít pro všechna +zvířata, podobně jako <code>snez</code>. +Bude nejlepší je přejmenovat:</p> +<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Zviratko</span><span class="p">:</span> + <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jmeno</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span> <span class="o">=</span> <span class="n">jmeno</span> + + <span class="k">def</span> <span class="nf">snez</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">jidlo</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">jidlo</span><span class="si">}</span><span class="s2"> mi chutná!"</span><span class="p">)</span> + + +<span class="k">class</span> <span class="nc">Kotatko</span><span class="p">(</span><span class="n">Zviratko</span><span class="p">):</span> + <span class="k">def</span> <span class="nf">udelej_zvuk</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: Mňau!"</span><span class="p">)</span> + + +<span class="k">class</span> <span class="nc">Stenatko</span><span class="p">(</span><span class="n">Zviratko</span><span class="p">):</span> + <span class="k">def</span> <span class="nf">udelej_zvuk</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">jmeno</span><span class="si">}</span><span class="s2">: Haf!"</span><span class="p">)</span> + + +<span class="n">zviratka</span> <span class="o">=</span> <span class="p">[</span><span class="n">Kotatko</span><span class="p">(</span><span class="s1">'Micka'</span><span class="p">),</span> <span class="n">Stenatko</span><span class="p">(</span><span class="s1">'Azorek'</span><span class="p">)]</span> + +<span class="k">for</span> <span class="n">zviratko</span> <span class="ow">in</span> <span class="n">zviratka</span><span class="p">:</span> + <span class="n">zviratko</span><span class="o">.</span><span class="n">udelej_zvuk</span><span class="p">()</span> + <span class="n">zviratko</span><span class="o">.</span><span class="n">snez</span><span class="p">(</span><span class="s1">'flákota'</span><span class="p">)</span> +</pre></div><p>Jak tenhle příklad naznačuje, psát nadtřídy, ze kterých se dobře dědí, +není jednoduché. Zvlášť to platí, kdyby se z nich mělo dědit v jiném +programu, než kde je nadtřída. +I z toho důvodu je dobré dědičnost používat hlavně v rámci svého kódu: +nedoporučuji dědit od tříd, které napsali ostatní (jako <code>bool</code> nebo +<code>pyglet.sprite.Sprite</code>), pokud autor nadtřídy výslovně nezmíní, že (a jak) se +z ní dědit má.</p> +<p>A to je zatím o třídách vše. Už toho víš dost na to, +aby sis napsal/a vlastní zoo :)</p> \ No newline at end of file diff --git a/lessons/install-editor/atom.html b/lessons/install-editor/atom.html new file mode 100644 index 00000000..f1bf6ec9 --- /dev/null +++ b/lessons/install-editor/atom.html @@ -0,0 +1,83 @@ +<h1 id="instalace_atomu">Instalace Atomu +<a href="#instalace_atomu" class="header-link">#</a> +</h1> +<p>Editor Atom +si stáhni z jeho <a href="https://atom.io">domovské stránky</a> +a nainstaluj.</p> +<h2 id="nastaveni">Nastavení +<a href="#nastaveni" class="header-link">#</a> +</h2> +<p>V Atomu se nemusí nic nastavovat, funguje „od výroby“ tak, jak má.</p> +<p>Odsazování a obarvování bude fungovat správně jen v souborech s koncovkou <code>.py</code> +(jako Python). +V jiných programovacích jazycích se totiž odsazuje i obarvuje jinak.</p> +<p>Proto jakmile v tomhle editoru vytvoříš nový soubor, +měl/a bys ho co nejdřív uložit pod správným jménem.</p> +<h2 id="kontrola_stylu_zdrojoveho_kodu">Kontrola stylu zdrojového kódu +<a href="#kontrola_stylu_zdrojoveho_kodu" class="header-link">#</a> +</h2> +<p>Jedna věc nám v Atomu přeci jen chybí: plugin pro kontrolu správného +stylu zdrojového kódu.</p> +<p>Tak jako čeština má Python typografická pravidla. +Například za čárkou se píše mezera, ale před ní ne. +Jsou nepovinná, program bude fungovat i při jejich nedodržení, +ale pomáhají psát přehledný kód, tak je dobré je dodržovat už od začátku. +Pravidla pro Python jsou popsána v dokumentu +<a href="https://www.python.org/dev/peps/pep-0008/">PEP8</a>.</p> +<p>Aby sis je nemusel/a všechny pamatovat, nainstaluj si plugin, +který tě na jejich porušení upozorní.</p> +<p>Nejprve je potřeba si nainstalovat speciální knihovnu, která se o kontrolu +dokáže postarat. Do příkazové řádky zadej následující:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>python<span class="w"> </span>-m<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>flake8 +</pre></div><p>A nyní si nainstaluj plugin do samotného editoru. V hlavní nabídce vyber +„Soubor > Nastavení<span class="en">/File > Settings</span>“ a v nabídce +uprostřed okna vyber poslední položku +„Instalovat<span class="en">/Install</span>“. Do vyhledávacího pole zadej +„linter-flake8“ a v seznamu nalezených pluginů klikni u položky stejného jména +na tlačítko „Instalovat<span class="en">/Install</span>“. Bude ještě potřeba +schválit instalaci všech závislostí, na které se Atom postupně zeptá.</p> +<h2 id="nacvik_odsazovani">Nácvik odsazování +<a href="#nacvik_odsazovani" class="header-link">#</a> +</h2> +<p>Jak už bylo zmíňeno, v Pythonu je důležité, kolika mezerami řádek začíná. +Proto se nám bude hodit vědět, jak rychle odsazovat bloky textu. +Pojďme si ukázat, jak na to.</p> +<p>Zkopíruj si do editoru tento text:</p> +<div class="highlight"><pre><code>Ofelie: +Ach princi! +Jak má se Vaše Výsost už tak dlouho? +Hamlet: +Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: +Mám od vás, princi, stále ještě dárky, +Jež dávno toužím vrátit. Prosím vás, +račte je přijmout teď. +Hamlet: +Kdo? Já? Já nikdy +vám nedal nic. +Ofelie: +Dal, Výsosti. A spolu s dárky slova +tak rozmilá, že každý z nich +měl jejich vůni. Ta teď vyvanula, +a tak je vracím. Dary nejbohatší +se mění v trety, když se dárce mračí. +Zde, Výsosti. +</code></pre></div><p><small>(úryvek ze hry Hamlet, napsal W. Shakespeare, překlad E. A. Saudek)</small></p> +<p>Tenhle text není moc přehledný, tak ho zkusíme poodsazovat, aby vypadal takhle:</p> +<div class="highlight"><pre><code>Ofelie: + Ach princi! + Jak má se Vaše Výsost už tak dlouho? +Hamlet: + Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: + Mám od vás, princi, stále ještě dárky, + Jež dávno toužím vrátit. Prosím vás, + račte je přijmout teď. +atd. +</code></pre></div><p>Abys odsadil/a jeden řádek, nastav kurzor na začátek řádku a stiskni +klávesu <kbd>Tab</kbd>. +Každým stisknutím řádek odsadíš o 4 mezery.</p> +<p>Odsadíš-li moc, pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd> odsazení zmenšíš.</p> +<p>Chceš-li odsadit víc řádků najednou, všechny je vyber a stiskni <kbd>Tab</kbd>. +I výběr můžeš „od-odsadit“ pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd>.</p> +<p>A to je vše! Teď máš nejen nastavený editor, ale umíš ho i používat.</p> \ No newline at end of file diff --git a/lessons/install-editor/gedit.html b/lessons/install-editor/gedit.html new file mode 100644 index 00000000..8d901bad --- /dev/null +++ b/lessons/install-editor/gedit.html @@ -0,0 +1,86 @@ +<h1 id="instalace_geditu">Instalace Geditu +<a href="#instalace_geditu" class="header-link">#</a> +</h1> +<p>Na Linuxu se Gedit instaluje jako ostatní programy:</p> +<dl> +<dt>Fedora</dt> +<dd><div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>dnf<span class="w"> </span>install<span class="w"> </span>gedit +</pre></div></dd> +<dt>Ubuntu</dt> +<dd><div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>gedit +</pre></div></dd> +</dl> +<p>Používáš-li jiný Linux, předpokládám že programy instalovat umíš. :)</p> +<p>Pro Windows a macOS se Gedit dá stáhnout z <a href="https://wiki.gnome.org/Apps/Gedit">domovské stránky</a>.</p> +<h2 id="nastaveni">Nastavení +<a href="#nastaveni" class="header-link">#</a> +</h2> +<p>Gedit se nastavuje v Předvolbách <span class="en">(Preferences)</span>.</p> +<p><span class="figure"><a href="naucse:static?filename=gedit_prefs.png"><img src="naucse:static?filename=gedit_prefs.png" alt=""></a></span></p> +<dl> +<dt>Číslování řádků</dt> +<dd><p>V sekci Zobrazit/<span class="en">View</span> vyber +Zobrazovat čísla řádků/<span class="en">Display Line Numbers</span>.</p> +<p><span class="figure"><a href="naucse:static?filename=gedit_linenums.png"><img src="naucse:static?filename=gedit_linenums.png" alt=""></a></span></p> +</dd> +<dt>Odsazování</dt> +<dd><p>V sekci Editor vyber:</p> +<ul> +<li>Šířka tabulátorů/<span class="en">Tab width</span>: 4</li> +<li>Vkládat mezery místo tabulátorů<span class="en">/Insert spaces instead of tabs</span></li> +<li>Povolit automatické odsazování<span class="en">/Enable automatic indentation</span></li> +</ul> +<p><span class="figure"><a href="naucse:static?filename=gedit_indent.png"><img src="naucse:static?filename=gedit_indent.png" alt=""></a></span></p> +</dd> +<dt>Obarvování</dt> +<dd><p>Obarvování funguje automaticky, ale způsob obarvování se vybírá podle +koncovky souboru – např. <code>.py</code> pro Python.</p> +<p>Proto jakmile v tomhle editoru vytvoříš nový soubor, měl/a bys ho co +nejdřív uložit pod správným jménem.</p> +</dd> +</dl> +<h2 id="nacvik_odsazovani">Nácvik odsazování +<a href="#nacvik_odsazovani" class="header-link">#</a> +</h2> +<p>Jak už bylo zmíňeno, v Pythonu je důležité, kolika mezerami řádek začíná. +Proto se nám bude hodit vědět, jak rychle odsazovat bloky textu. +Pojďme si ukázat, jak na to.</p> +<p>Zkopíruj si do editoru tento text:</p> +<div class="highlight"><pre><code>Ofelie: +Ach princi! +Jak má se Vaše Výsost už tak dlouho? +Hamlet: +Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: +Mám od vás, princi, stále ještě dárky, +Jež dávno toužím vrátit. Prosím vás, +račte je přijmout teď. +Hamlet: +Kdo? Já? Já nikdy +vám nedal nic. +Ofelie: +Dal, Výsosti. A spolu s dárky slova +tak rozmilá, že každý z nich +měl jejich vůni. Ta teď vyvanula, +a tak je vracím. Dary nejbohatší +se mění v trety, když se dárce mračí. +Zde, Výsosti. +</code></pre></div><p><small>(úryvek ze hry Hamlet, napsal W. Shakespeare, překlad E. A. Saudek)</small></p> +<p>Tenhle text není moc přehledný, tak ho zkusíme poodsazovat, aby vypadal takhle:</p> +<div class="highlight"><pre><code>Ofelie: + Ach princi! + Jak má se Vaše Výsost už tak dlouho? +Hamlet: + Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: + Mám od vás, princi, stále ještě dárky, + Jež dávno toužím vrátit. Prosím vás, + račte je přijmout teď. +atd. +</code></pre></div><p>Abys odsadil/a jeden řádek, nastav kurzor na začátek řádku a stiskni +klávesu <kbd>Tab</kbd>. +Každým stisknutím řádek odsadíš o 4 mezery.</p> +<p>Odsadíš-li moc, pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd> odsazení zmenšíš.</p> +<p>Chceš-li odsadit víc řádků najednou, všechny je vyber a stiskni <kbd>Tab</kbd>. +I výběr můžeš „od-odsadit“ pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd>.</p> +<p>A to je vše! Teď máš nejen nastavený editor, ale umíš ho i používat.</p> \ No newline at end of file diff --git a/lessons/beginners/install-editor/static/gedit_indent.png b/lessons/install-editor/gedit_indent.png similarity index 100% rename from lessons/beginners/install-editor/static/gedit_indent.png rename to lessons/install-editor/gedit_indent.png diff --git a/lessons/beginners/install-editor/static/gedit_linenums.png b/lessons/install-editor/gedit_linenums.png similarity index 100% rename from lessons/beginners/install-editor/static/gedit_linenums.png rename to lessons/install-editor/gedit_linenums.png diff --git a/lessons/beginners/install-editor/static/gedit_prefs.png b/lessons/install-editor/gedit_prefs.png similarity index 100% rename from lessons/beginners/install-editor/static/gedit_prefs.png rename to lessons/install-editor/gedit_prefs.png diff --git a/lessons/install-editor/index.html b/lessons/install-editor/index.html new file mode 100644 index 00000000..1446978a --- /dev/null +++ b/lessons/install-editor/index.html @@ -0,0 +1,104 @@ +<h1 id="instalace_editoru">Instalace editoru +<a href="#instalace_editoru" class="header-link">#</a> +</h1> +<p>Editor, program na úpravu textu, je základní pomůcka +každého programátora, +takže je dobré do něj investovat trochu času.</p> +<p>Je víceméně jedno, který programátorský editor budeš používat. +Pokud už nějaký oblíbený máš, stačí ho jen nastavit; +jestli ne, nějaký ti doporučíme. +Pokud ale používáš Poznámkový blok (Notepad) z Windows, +nebo TextEdit (editor předinstalovaný v macOS), +nebude ti stačit. +Stejně tak nejsou vhodné programy jako Word či Writer.</p> +<h2 id="co_programatorsky_editor_umi">Co programátorský editor umí +<a href="#co_programatorsky_editor_umi" class="header-link">#</a> +</h2> +<p>Editor pro programátory nám umožňuje upravovat <em>prostý text</em> – písmenka. +Na rozdíl od programů jako Word, Writer či Pages neumožňuje text <em>formátovat</em>, +tedy dělat nadpisy, obarvovat, zvětšovat font, vkládat obrázky a podobně.</p> +<p>Pomocí editoru budeme zadávat počítači příkazy, takže formátování nepotřebujeme. +Porovnej sám/sama, jaký je rozdíl mezi následujícími příkazy +pro někoho, kdo se jimi má řídit:</p> +<ul> +<li>Nakresli mi beránka!</li> +<li><font color="green">Nakresli <big><big>mi</big> <u>beránka</u>!</big></font></li> +</ul> +<p>I když neumí formátování, neznamená to, že jsou naše editory úplně „hloupé“ +nástroje. +Aby se nám programy upravovaly pohodlněji, mají několik vychytávek:</p> +<dl> +<dt>Podpora více souborů</dt> +<dd>Větší projekty sestávají z více souborů, které můžeš mít v editoru +otevřené všechny najednou.</dd> +<dt>Číslování řádků</dt> +<dd>Před každým řádkem se ukazuje číslo. +To se bude velice hodit, až Python bude nadávat, že chyba je na řádku 183.</dd> +<dt>Odsazování</dt> +<dd>V Pythonu je důležité, kolika mezerami řádek začíná. +Správně nastavený editor nám odsazování značně zjednoduší.</dd> +<dt>Obarvování</dt> +<dd>Ačkoli nemůžeme u jednotlivých písmenek nastavovat barvu přímo, editor nám +obarvením může napovědět, jak našim instrukcím bude počítač rozumět. +Ale je to jenom nápověda: +programátor s jinak nastaveným editorem může mít stejný soubor obarvený +docela jinak.</dd> +</dl> +<div class="admonition note"><p>Pro ilustraci, takhle může v editoru vypadat kousek kódu:</p> +<div class="highlight"><pre><span></span> <span class="mi">1</span> <span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/courses/<course:course>/'</span><span class="p">)</span> + <span class="mi">2</span> <span class="k">def</span> <span class="nf">course_page</span><span class="p">(</span><span class="n">course</span><span class="p">):</span> + <span class="mi">3</span> <span class="k">try</span><span class="p">:</span> + <span class="mi">4</span> <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span> + <span class="mi">5</span> <span class="s1">'course.html'</span><span class="p">,</span> + <span class="mi">6</span> <span class="n">course</span><span class="o">=</span><span class="n">course</span><span class="p">,</span> + <span class="mi">7</span> <span class="n">plan</span><span class="o">=</span><span class="n">course</span><span class="o">.</span><span class="n">sessions</span><span class="p">,</span> + <span class="mi">8</span> <span class="p">)</span> + <span class="mi">9</span> <span class="k">except</span> <span class="n">TemplateNotFound</span><span class="p">:</span> + <span class="mi">10</span> <span class="n">abort</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span> +</pre></div></div><h2 id="volba_a_nastaveni_editoru">Volba a nastavení editoru +<a href="#volba_a_nastaveni_editoru" class="header-link">#</a> +</h2> +<p>Vybereš-li editor, klikni na jeho jméno a dostaneš se na instrukce ke stažení +a nastavení. +(Na tuhle stránku se pak už nemusíš vracet.)</p> +<ul> +<li><a href="naucse:page?lesson=beginners/install-editor&page=vscode">Visual Studio Code</a> – doporučený editor pro +Windows a macOS (a vhodný i pro Linux). +V poslední době je to asi nejpopulárnější editor kódu. +Nabízí mnoho funkcí a má velkou základnu uživatelů a vývojářů, +takže se neustále vylepšuje.</li> +</ul> +<p>Na Linuxu budeš mít pravděpodobně už nainstalovaný Gedit nebo Kate. +Zkus se podívat do systémové nabídky, jestli jeden z nich máš (případně je +spusť z příkazové řádky jako <code>gedit</code>, resp. <code>kate</code>). +Pokud ano, klikni na odkaz níže a editor si nastav. +Nemáš-li ani jeden, vyber Visual Studio Code (viz výše).</p> +<ul> +<li><a href="naucse:page?lesson=beginners/install-editor&page=gedit">Gedit</a> – bývá na systémech s prostředím GNOME.</li> +<li><a href="naucse:page?lesson=beginners/install-editor&page=kate">Kate</a> – bývá na systémech s prostředím KDE.</li> +</ul> +<p>Existují i jiné editory, na které máme návody +nebo jsme je doporučovali ve starších verzích těchto materiálů. +Pokud se jeden z nich rozhodneš použít, neuděláš chybu:</p> +<ul> +<li><a href="naucse:page?lesson=beginners/install-editor&page=atom">Atom</a> – kvalitní editor s jednoduchým designem</li> +<li><a href="naucse:page?lesson=beginners/install-editor&page=notepad-plus-plus">Notepad++</a> – nenáročný editor +pro Windows vhodný pro pomalejší počítače</li> +</ul> +<p>Máš-li už svůj oblíbený editor – Vim, Emacs, Geany, apod., použij ten:</p> +<ul> +<li><a href="naucse:page?lesson=beginners/install-editor&page=others">Ostatní</a> – máš-li jiný editor, zkontroluj +si, že je správně nastaven.</li> +</ul> +<h3 id="ide">IDE +<a href="#ide" class="header-link">#</a> +</h3> +<p>Existují i složitější a mocnější editory, takzvané <em>IDE</em> (angl. <em>Integrated +Development Environment</em>, integrované vývojové prostředí), +třeba <a href="https://www.jetbrains.com/pycharm/">PyCharm</a>, <a href="https://eclipse.org/">Eclipse</a> nebo <a href="https://www.kdevelop.org/">KDevelop</a>. +Umí spoustu pokročilých funkcí, které programátorům pomáhají: +našeptávání, přejmenovávání, spouštění programů, správu virtuálních prostředí +a podobně. +Na začátek ale nejsou moc vhodné.</p> +<p>Chceš-li takový editor přesto použít, měl/a bys ho už poměrně dobře znát: +vědět, co za tebe dělá editor a jak to spravit, až něco udělá špatně.</p> \ No newline at end of file diff --git a/lessons/install-editor/kate.html b/lessons/install-editor/kate.html new file mode 100644 index 00000000..36a0a058 --- /dev/null +++ b/lessons/install-editor/kate.html @@ -0,0 +1,96 @@ +<h1 id="instalace_kate">Instalace Kate +<a href="#instalace_kate" class="header-link">#</a> +</h1> +<p>Na Linuxu se Kate instaluje jako ostatní programy:</p> +<dl> +<dt>Fedora</dt> +<dd><div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>dnf<span class="w"> </span>install<span class="w"> </span>kate +</pre></div></dd> +<dt>Ubuntu</dt> +<dd><div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>kate +</pre></div></dd> +</dl> +<p>Používáš-li jiný Linux, předpokládám že programy instalovat umíš. :)</p> +<p>Pro Windows a macOS se Kate dá stáhnout z <a href="https://kate-editor.org/get-it/">domovské stránky</a>.</p> +<h2 id="nastaveni">Nastavení +<a href="#nastaveni" class="header-link">#</a> +</h2> +<dl> +<dt>Číslování řádků</dt> +<dd>V menu Pohled<span class="en">/View</span> vyber +Ukazovat čísla řádek<span class="en">/Show Line Numbers</span>.</dd> +<dt>Odsazování</dt> +<dd><p>V Menu Nastavení<span class="en">/Settings</span> vyber +Nastavit 'Kate'<span class="en">/Configure Kate</span>.</p> +<p>Tam v Úpravy<span class="en">/Editing</span> vyber +Odsazování<span class="en">/Indentation</span>.</p> +<p>Tam nastav:</p> +<ul> +<li>Výchozí režim odsazení<span class="en">/Default indentation mode</span>: Python</li> +<li>Odsazovat pomocí<span class="en">/Indent using</span>: Mezer<span class="en">/Spaces</span></li> +<li>Šířka tabulátoru<span class="en">/Tab Width</span>: 4 znaky</li> +<li>Odsadit pomocí<span class="en">/Indentation width</span>: 4 znaky</li> +<li>Klávesa Backspace zpětně odsazuje v úvodních mezerách<span class="en">/Backspace key in leading blank space unindents</span></li> +</ul> +</dd> +<dt>Obarvování</dt> +<dd><p>Obarvování funguje automaticky, ale způsob obarvování se vybírá podle +koncovky souboru – např. <code>.py</code> pro Python.</p> +<p>Proto, jakmile v tomhle editoru vytvoříš nový soubor, měl/a bys ho co +nejdřív uložit pod správným jménem.</p> +</dd> +<dt>Psaní < ve Windows</dt> +<dd>Pokud používáš českou klávesnici, je možné, že ti ve výchozím nastavení +nepůjde v Kate napsat znak <code><</code>. Při stisknutí kláves +<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>,</kbd> nebo +<kbd>AltGr</kbd>+<kbd>,</kbd> se místo napsání <code><</code> zobrazí okno Nastavit +klávesové zkratky<span class="en">/Configure keybord shortcuts</span>. V +tom případě v tomto okně najdi klávesovou zkratku pro Nastavit klávesové +zkratky<span class="en">/Configure keybord shortcuts</span> a změn ji na +Žádná<span class="en">/None</span>.</dd> +</dl> +<h2 id="nacvik_odsazovani">Nácvik odsazování +<a href="#nacvik_odsazovani" class="header-link">#</a> +</h2> +<p>Jak už bylo zmíňeno, v Pythonu je důležité, kolika mezerami řádek začíná. +Proto se nám bude hodit vědět, jak rychle odsazovat bloky textu. +Pojďme si ukázat, jak na to.</p> +<p>Zkopíruj si do editoru tento text:</p> +<div class="highlight"><pre><code>Ofelie: +Ach princi! +Jak má se Vaše Výsost už tak dlouho? +Hamlet: +Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: +Mám od vás, princi, stále ještě dárky, +Jež dávno toužím vrátit. Prosím vás, +račte je přijmout teď. +Hamlet: +Kdo? Já? Já nikdy +vám nedal nic. +Ofelie: +Dal, Výsosti. A spolu s dárky slova +tak rozmilá, že každý z nich +měl jejich vůni. Ta teď vyvanula, +a tak je vracím. Dary nejbohatší +se mění v trety, když se dárce mračí. +Zde, Výsosti. +</code></pre></div><p><small>(úryvek ze hry Hamlet, napsal W. Shakespeare, překlad E. A. Saudek)</small></p> +<p>Tenhle text není moc přehledný, tak ho zkusíme poodsazovat, aby vypadal takhle:</p> +<div class="highlight"><pre><code>Ofelie: + Ach princi! + Jak má se Vaše Výsost už tak dlouho? +Hamlet: + Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: + Mám od vás, princi, stále ještě dárky, + Jež dávno toužím vrátit. Prosím vás, + račte je přijmout teď. +atd. +</code></pre></div><p>Abys odsadil/a jeden řádek, nastav kurzor na začátek řádku a stiskni +klávesu <kbd>Tab</kbd>. +Každým stisknutím řádek odsadíš o 4 mezery.</p> +<p>Odsadíš-li moc, pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd> odsazení zmenšíš.</p> +<p>Chceš-li odsadit víc řádků najednou, všechny je vyber a stiskni <kbd>Tab</kbd>. +I výběr můžeš „od-odsadit“ pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd>.</p> +<p>A to je vše! Teď máš nejen nastavený editor, ale umíš ho i používat.</p> \ No newline at end of file diff --git a/lessons/install-editor/notepad-plus-plus.html b/lessons/install-editor/notepad-plus-plus.html new file mode 100644 index 00000000..3f40c8e1 --- /dev/null +++ b/lessons/install-editor/notepad-plus-plus.html @@ -0,0 +1,65 @@ +<h1 id="instalace_notepadu">Instalace Notepadu++ +<a href="#instalace_notepadu" class="header-link">#</a> +</h1> +<p>Notepad++ je k dispozici pouze pro Windows.</p> +<p>Stáhni jej z jeho <a href="https://notepad-plus-plus.org/">domovské stránky</a> +a nainstaluj.</p> +<h2 id="nastaveni">Nastavení +<a href="#nastaveni" class="header-link">#</a> +</h2> +<dl> +<dt>Odsazování</dt> +<dd>V menu Nastavení zvol Předvolby a pak nastav +„Nastavení tabulátoru<span class="en">/Tab Settings</span>“ na +„Zaměnit za mezery<span class="en">/Replace by Space</span>“.</dd> +</dl> +<p>Obarvování bude fungovat automaticky v souborech s koncovkou <code>.py</code> +(jako Python). +V jiných programovacích jazycích se totiž odsazuje i obarvuje jinak.</p> +<p>Proto, jakmile v tomhle editoru vytvoříš nový soubor, +měl/a bys ho co nejdřív uložit pod správným jménem.</p> +<h2 id="nacvik_odsazovani">Nácvik odsazování +<a href="#nacvik_odsazovani" class="header-link">#</a> +</h2> +<p>Jak už bylo zmíňeno, v Pythonu je důležité, kolika mezerami řádek začíná. +Proto se nám bude hodit vědět, jak rychle odsazovat bloky textu. +Pojďme si ukázat, jak na to.</p> +<p>Zkopíruj si do editoru tento text:</p> +<div class="highlight"><pre><code>Ofelie: +Ach princi! +Jak má se Vaše Výsost už tak dlouho? +Hamlet: +Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: +Mám od vás, princi, stále ještě dárky, +Jež dávno toužím vrátit. Prosím vás, +račte je přijmout teď. +Hamlet: +Kdo? Já? Já nikdy +vám nedal nic. +Ofelie: +Dal, Výsosti. A spolu s dárky slova +tak rozmilá, že každý z nich +měl jejich vůni. Ta teď vyvanula, +a tak je vracím. Dary nejbohatší +se mění v trety, když se dárce mračí. +Zde, Výsosti. +</code></pre></div><p><small>(úryvek ze hry Hamlet, napsal W. Shakespeare, překlad E. A. Saudek)</small></p> +<p>Tenhle text není moc přehledný, tak ho zkusíme poodsazovat, aby vypadal takhle:</p> +<div class="highlight"><pre><code>Ofelie: + Ach princi! + Jak má se Vaše Výsost už tak dlouho? +Hamlet: + Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: + Mám od vás, princi, stále ještě dárky, + Jež dávno toužím vrátit. Prosím vás, + račte je přijmout teď. +atd. +</code></pre></div><p>Abys odsadil/a jeden řádek, nastav kurzor na začátek řádku a stiskni +klávesu <kbd>Tab</kbd>. +Každým stisknutím řádek odsadíš o 4 mezery.</p> +<p>Odsadíš-li moc, pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd> odsazení zmenšíš.</p> +<p>Chceš-li odsadit víc řádků najednou, všechny je vyber a stiskni <kbd>Tab</kbd>. +I výběr můžeš „od-odsadit“ pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd>.</p> +<p>A to je vše! Teď máš nejen nastavený editor, ale umíš ho i používat.</p> \ No newline at end of file diff --git a/lessons/install-editor/others.html b/lessons/install-editor/others.html new file mode 100644 index 00000000..b5c2bb0e --- /dev/null +++ b/lessons/install-editor/others.html @@ -0,0 +1,93 @@ +<h1 id="instalace_editoru">Instalace editoru +<a href="#instalace_editoru" class="header-link">#</a> +</h1> +<p>Používáš-li editor, pro který nemáme instrukce, budeš ho muset nastavit +sám/sama. +Tady je pár tipů, na co si dát pozor.</p> +<h2 id="nastaveni">Nastavení +<a href="#nastaveni" class="header-link">#</a> +</h2> +<h2 id="cislovani_radku">Číslování řádků +<a href="#cislovani_radku" class="header-link">#</a> +</h2> +<p>Ujisti se, že ti editor čísluje řádky. +Pokud ne, podívej se do nastavení a zjisti, jak se to zapíná.</p> +<h2 id="obarvovani">Obarvování +<a href="#obarvovani" class="header-link">#</a> +</h2> +<p>Ulož soubor s koncovkou <code>.py</code> – například <code>zkouska.py</code> – a zkopíruj do něj +následující program:</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span> + <span class="k">return</span> <span class="s2">"abc"</span> <span class="o">*</span> <span class="mi">2</span> +</pre></div><p>Jestli se text automaticky obarví (klidně jinými barvami než tady), +je tvůj editor nastavený správně. +Jinak se podívej do nastavení a zjisti, jak se to zapíná.</p> +<h2 id="odsazovani">Odsazování +<a href="#odsazovani" class="header-link">#</a> +</h2> +<p>Stisknutím klávesy <kbd>Tab</kbd> na <em>začatku řádku</em> se vloží 4 mezery. +Pro psaní a sdílení kódu v Pythonu je důležité, +aby byly čtyři a aby to byly opravdu mezery.</p> +<p>Jestli to jsou mezery, se dá zjistit tak, že odsazení na začátku vybereš myší. +Jde-li vybírat po jednotlivých mezerách, je všechno v pořádku.</p> +<p>Nejde-li vybírat po jednotlivých mezerách, nebo pokud se jich po stisknutí +<kbd>Tab</kbd> vloží jiný počet než 4, podívej se do nastavení po možnostech +jako „velikost odsazení“ nebo „nahrazovat tabulátory za mezery”.</p> +<h2 id="kontrola_stylu_zdrojoveho_kodu">Kontrola stylu zdrojového kódu +<a href="#kontrola_stylu_zdrojoveho_kodu" class="header-link">#</a> +</h2> +<p>Editory často podporují instalaci pluginů, které mohou psaní kódu usnadnit +a pomoci s jeho kontrolou. +Jeden z neužitečnějších je plugin pro kontrolu správného stylu zdrojového kódu.</p> +<p>Tak jako čeština má Python typografická providla. +Například za čárkou se píše mezera, ale před ní ne. +Jsou nepovinná, program bude fungovat i při jejich nedodržení, +ale pomáhají psát přehledný kód, tak je dobré je dodržovat už od začátku. +Tato pravidla jsou popsána +v dokumentu <a href="https://www.python.org/dev/peps/pep-0008/">PEP8</a>.</p> +<p>Zkus takový plugin pro svůj editor najít a nainstalovat.</p> +<h2 id="nacvik_odsazovani">Nácvik odsazování +<a href="#nacvik_odsazovani" class="header-link">#</a> +</h2> +<p>Jak už bylo zmíňeno, v Pythonu je důležité, kolika mezerami řádek začíná. +Proto se nám bude hodit vědět, jak rychle odsazovat bloky textu. +Pojďme si ukázat, jak na to.</p> +<p>Zkopíruj si do editoru tento text:</p> +<div class="highlight"><pre><code>Ofelie: +Ach princi! +Jak má se Vaše Výsost už tak dlouho? +Hamlet: +Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: +Mám od vás, princi, stále ještě dárky, +Jež dávno toužím vrátit. Prosím vás, +račte je přijmout teď. +Hamlet: +Kdo? Já? Já nikdy +vám nedal nic. +Ofelie: +Dal, Výsosti. A spolu s dárky slova +tak rozmilá, že každý z nich +měl jejich vůni. Ta teď vyvanula, +a tak je vracím. Dary nejbohatší +se mění v trety, když se dárce mračí. +Zde, Výsosti. +</code></pre></div><p><small>(úryvek ze hry Hamlet, napsal W. Shakespeare, překlad E. A. Saudek)</small></p> +<p>Tenhle text není moc přehledný, tak ho zkusíme poodsazovat, aby vypadal takhle:</p> +<div class="highlight"><pre><code>Ofelie: + Ach princi! + Jak má se Vaše Výsost už tak dlouho? +Hamlet: + Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: + Mám od vás, princi, stále ještě dárky, + Jež dávno toužím vrátit. Prosím vás, + račte je přijmout teď. +atd. +</code></pre></div><p>Abys odsadil/a jeden řádek, nastav kurzor na začátek řádku a stiskni +klávesu <kbd>Tab</kbd>. +Každým stisknutím řádek odsadíš o 4 mezery.</p> +<p>Odsadíš-li moc, pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd> odsazení zmenšíš.</p> +<p>Chceš-li odsadit víc řádků najednou, všechny je vyber a stiskni <kbd>Tab</kbd>. +I výběr můžeš „od-odsadit“ pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd>.</p> +<p>A to je vše! Teď máš nejen nastavený editor, ale umíš ho i používat.</p> \ No newline at end of file diff --git a/lessons/install-editor/vscode.html b/lessons/install-editor/vscode.html new file mode 100644 index 00000000..3d9ebd4b --- /dev/null +++ b/lessons/install-editor/vscode.html @@ -0,0 +1,69 @@ +<h1 id="instalace_visual_studio_code">Instalace Visual Studio Code +<a href="#instalace_visual_studio_code" class="header-link">#</a> +</h1> +<h2 id="stazeni_a_instalace">Stažení a instalace +<a href="#stazeni_a_instalace" class="header-link">#</a> +</h2> +<p>Editor si můžeš stáhnout z jeho <a href="https://code.visualstudio.com/">domovské stránky</a>. +Vyber na ní zelené tlačítko Download a vyber instalátor pro svůj systém. +Dále se řiď instrukcemi instalátoru jako u každého jiného programu.</p> +<h2 id="nastaveni">Nastavení +<a href="#nastaveni" class="header-link">#</a> +</h2> +<p>Ve Visual Studio Code se nemusí nic nastavovat, funguje „od výroby“ tak, jak má.</p> +<h3 id="odesilani_telemetrickych_dat">Odesílání telemetrických dat +<a href="#odesilani_telemetrickych_dat" class="header-link">#</a> +</h3> +<p>Tento textový editor ale odesílá data o tvém používání (<a href="https://privacy.microsoft.com/en-us/privacystatement">nejspíš včetně např. +obsahu otevřených souborů</a>). +Pokud si nepřeješ aby se data odesílala, můžeš odesílání zrušit:</p> +<ul> +<li>Otevři <strong>File</strong> > <strong>Preferences</strong> > <strong>Settings</strong> (macOS: <strong>Code</strong> > <strong>Preferences</strong> > <strong>Settings</strong>).</li> +<li>Vyhledej <code>telemetry.enableTelemetry</code> a odškrtni tento záznam.</li> +</ul> +<p>Viz též <a href="https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting">původni postup v angličtině</a>.</p> +<h2 id="nacvik_odsazovani">Nácvik odsazování +<a href="#nacvik_odsazovani" class="header-link">#</a> +</h2> +<p>Jak už bylo zmíňeno, v Pythonu je důležité, kolika mezerami řádek začíná. +Proto se nám bude hodit vědět, jak rychle odsazovat bloky textu. +Pojďme si ukázat, jak na to.</p> +<p>Zkopíruj si do editoru tento text:</p> +<div class="highlight"><pre><code>Ofelie: +Ach princi! +Jak má se Vaše Výsost už tak dlouho? +Hamlet: +Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: +Mám od vás, princi, stále ještě dárky, +Jež dávno toužím vrátit. Prosím vás, +račte je přijmout teď. +Hamlet: +Kdo? Já? Já nikdy +vám nedal nic. +Ofelie: +Dal, Výsosti. A spolu s dárky slova +tak rozmilá, že každý z nich +měl jejich vůni. Ta teď vyvanula, +a tak je vracím. Dary nejbohatší +se mění v trety, když se dárce mračí. +Zde, Výsosti. +</code></pre></div><p><small>(úryvek ze hry Hamlet, napsal W. Shakespeare, překlad E. A. Saudek)</small></p> +<p>Tenhle text není moc přehledný, tak ho zkusíme poodsazovat, aby vypadal takhle:</p> +<div class="highlight"><pre><code>Ofelie: + Ach princi! + Jak má se Vaše Výsost už tak dlouho? +Hamlet: + Děkují poníženě: skvěle, skvěle, skvěle. +Ofelie: + Mám od vás, princi, stále ještě dárky, + Jež dávno toužím vrátit. Prosím vás, + račte je přijmout teď. +atd. +</code></pre></div><p>Abys odsadil/a jeden řádek, nastav kurzor na začátek řádku a stiskni +klávesu <kbd>Tab</kbd>. +Každým stisknutím řádek odsadíš o 4 mezery.</p> +<p>Odsadíš-li moc, pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd> odsazení zmenšíš.</p> +<p>Chceš-li odsadit víc řádků najednou, všechny je vyber a stiskni <kbd>Tab</kbd>. +I výběr můžeš „od-odsadit“ pomocí <kbd>Shift</kbd>+<kbd>Tab</kbd>.</p> +<p>A to je vše! Teď máš nejen nastavený editor, ale umíš ho i používat.</p> \ No newline at end of file diff --git a/lessons/install/index.1.html b/lessons/install/index.1.html new file mode 100644 index 00000000..47ce4fe1 --- /dev/null +++ b/lessons/install/index.1.html @@ -0,0 +1,66 @@ +<p>V tomto kurzu budeme používat <em>virtuální prostředí</em>. +Jedná se o oddělené prostředí pro Python, kam se dají instalovat jednotlivé +knihovny, které jsou potom aktivní jen uvnitř. +Použití prostředí má dvě hlavní výhody:</p> +<ul> +<li><p>Je-li prostředí aktivované, +příkaz <code>python</code> spustí verzi Pythonu, se kterou bylo prostředí nainstalováno. +Takže pak např. v materiálech nemusíme mít speciální instrukce pro +Linux (<code>python3</code>) a Windows (<code>py -3</code>).</p> +</li> +<li><p>Instalace knihoven nezasahuje do systémového nastavení, ani do jiných +virtuálních prostředí. +Můžeš tak oddělit jednotlivé projekty; v každém prostředí můžou být +nainstalované jiné verze knihoven. +A když se něco pokazí, adresář s virtuálním prostředím můžeš prostě smazat +a vytvořit znovu.</p> +</li> +</ul> +<p>Následují zrychlené instrukce; předpokládám, že Python (3.6 a vyšší) +už máš nainstalovaný a že znáš základy práce +s <a href="naucse:page?lesson=beginners/cmdline">příkazovou řádkou</a>.</p> +<p>Podrobný postup instalace Pythonu a vytvoření prostředí je je popsán +v příslušné <a href="naucse:page?lesson=beginners/install">lekci pro začátečníky</a>. +Modul <code>venv</code> je součást <a href="https://docs.python.org/3/library/venv.html">standardní knihovny</a>.</p> +<h2 id="unix_linux_macos">Unix (Linux, macOS) +<a href="#unix_linux_macos" class="header-link">#</a> +</h2> +<p>Zkontroluj, že máš modul <code>ensurepip</code>:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>python3.7<span class="w"> </span>-m<span class="w"> </span>ensurepip<span class="w"> </span>--version +<span class="go">pip 18.0 (nebo i jiná verze)</span> +</pre></div><p>Jestli ne, postupuj podle <a href="naucse:page?lesson=beginners/install">lekce pro začátečníky</a> – +jsou tam podrobnější instrukce. +Jinak použij:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>mkdir<span class="w"> </span>project +<span class="gp">$ </span><span class="nb">cd</span><span class="w"> </span>project +<span class="gp">$ </span>python3.7<span class="w"> </span>-m<span class="w"> </span>venv<span class="w"> </span>__venv__<span class="w"> </span><span class="c1"># vytvoření virtualenvu -- použij Python 3 dle systému</span> +<span class="gp">$ </span>.<span class="w"> </span>__venv__/bin/activate<span class="w"> </span><span class="c1"># aktivace</span> +<span class="gp gp-VirtualEnv">(__venv__)</span><span class="gp">$ </span>python<span class="w"> </span>-m<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>requests<span class="w"> </span><span class="c1"># příkaz na instalaci balíčků puštěný ve virtualenvu</span> +<span class="gp gp-VirtualEnv">(__venv__)</span><span class="gp">$ </span>...<span class="w"> </span><span class="c1"># práce "uvnitř"</span> +<span class="gp gp-VirtualEnv">(__venv__)</span><span class="gp">$ </span>deactivate<span class="w"> </span><span class="c1"># vypnutí virtualenvu</span> +</pre></div><h2 id="windows">Windows +<a href="#windows" class="header-link">#</a> +</h2> +<div class="highlight"><pre><span></span><span class="gp">> </span>mkdir project +<span class="gp">> </span>cd project +<span class="gp">> </span>py -3 -m venv __venv__ +<span class="gp">> </span>__venv__\Scripts\activate +<span class="gp">(__venv__)> </span>python -m pip install requests <span class="c"># příkaz na instalaci balíčků puštěný ve virtualenvu</span> +<span class="gp">(__venv__)> </span>... <span class="c"># práce "uvnitř"</span> +<span class="gp">(__venv__)> </span>deactivate <span class="c"># vypnutí virtualenvu</span> +</pre></div><h2 id="poznamky">Poznámky +<a href="#poznamky" class="header-link">#</a> +</h2> +<p>Příkaz <code>. __venv__/bin/activate</code> budeš muset zadat vždy, než začneš na projektu +pracovat.</p> +<p>Ono <code>__venv__</code> je jen jméno adresáře. Můžeš si ho pojmenovat jak chceš; dokonce +nemusí být v rámci adresáře s projektem. +Někteří lidé mají všechny virtuální prostředí na jednom místě; dokonce existuje +nástroj <a href="https://virtualenvwrapper.readthedocs.io/en/latest/">virtualenvwrapper</a> na správu takového řešení, případně <a href="https://pipenv.readthedocs.io/en/latest/">pipenv</a>, +který <em>Python Packaging Authority</em> <a href="https://packaging.python.org/tutorials/managing-dependencies/">doporučuje</a> pro vývoj aplikací (my ale +budeme vyvíjet i knihovny).</p> +<p>Autoři tohoto textu tedy doporučují <code>__venv__</code> v adresáři s projektem. +Při použití v kombinaci s Gitem nezapomeň tento adresář přidat do souboru +<code>.gitignore</code>.</p> +<p>Příkaz <code>python -m pip install</code> nainstaluje danou knihovnu – může to být i jiná +než <code>requests</code> jako výše.</p> \ No newline at end of file diff --git a/lessons/install/index.2.html b/lessons/install/index.2.html new file mode 100644 index 00000000..c00835e2 --- /dev/null +++ b/lessons/install/index.2.html @@ -0,0 +1,90 @@ +<h1 id="git">Git +<a href="#git" class="header-link">#</a> +</h1> +<p>Další program, který budeme později potřebovat, +nám později umožní (mimojiné) spolupracovat +na vznikajících programech s ostatními. +Jmenuje se Git. +Pojďme si ho nainstalovat a nastavit.</p> +<p>Instalace je různá pro různé operační systémy, vyber ten svůj.</p> +<h2 id="linux">Linux +<a href="#linux" class="header-link">#</a> +</h2> +<p>Instalaci na Linux zvládneme jedním příkazem:</p> +<p><strong>Fedora, RHEL</strong>:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>dnf<span class="w"> </span>install<span class="w"> </span>git<span class="w"> </span>gitk<span class="w"> </span>git-gui<span class="w"> </span>nano +</pre></div><p><strong>Ubuntu, Debian</strong>:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>git<span class="w"> </span>gitk<span class="w"> </span>git-gui<span class="w"> </span>nano +</pre></div><p>U jiných Linuxů předpokládám, že instalovat umíš; nainstaluj si <em>git</em>, +<em>gitk</em>, <em>git gui</em> a <em>nano</em>.</p> +<p>Jestli máš nainstalováno, ještě nastav Gitu editor. +Pokud nemáš rád/a Vim (nebo nevíš co to je), +zadej tento příkaz:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>config<span class="w"> </span>--global<span class="w"> </span>core.editor<span class="w"> </span>nano +</pre></div><p>Dál pokračuj obecným <a href="#config">nastavením</a> níže.</p> +<h2 id="windows">Windows +<a href="#windows" class="header-link">#</a> +</h2> +<p>Jdi na stránku <a href="https://git-scm.org">git-scm.org</a>, stáhni si +Git a nainstaluj si ho.</p> +<p>Při instalaci se ujisti, že jsou vybrány tyto volby:</p> +<ul> +<li>Adjusting your PATH enviroment: Git from the command line and also from 3rd-party software</li> +<li>Configuring the line ending conversions: Checkout Windows-style, commit Unix-style line endings</li> +</ul> +<p>Ostatní možnosti neměň.</p> +<p>Potom Gitu nastav editor. +Máš-li otevřenou příkazovou řádku, zavři ji a otevři novou. +(Instalace mění systémové nastavení, které se musí načíst znovu.) +V nové příkazové řádce zadej:</p> +<div class="highlight"><pre><span></span><span class="go">> git config --global core.editor notepad</span> +<span class="go">> git config --global format.commitMessageColumns 80</span> +<span class="go">> git config --global gui.encoding utf-8</span> +</pre></div><p>A teď pokračuj v sekci <a href="#config">Nastavení</a> níže – macOS přeskoč.</p> +<h2 id="macos">macOS +<a href="#macos" class="header-link">#</a> +</h2> +<p>Spusť v příkazové řádce <code>git</code>. +Je-li už nainstalovaný, dozvíš se, jak ho používat +(výpis začíná <code>usage</code>). +Jinak ho nainstaluj pomocí Homebrew:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>brew<span class="w"> </span>install<span class="w"> </span>git<span class="w"> </span>git-gui +</pre></div><p>Nainstalovanému Gitu je ještě potřeba nastavit editor (zadej <code>nano</code>, +i když sis v rámci instalace editoru nainstaloval/a např. Atom). +Dělá se to tímto příkazem:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>config<span class="w"> </span>--global<span class="w"> </span>core.editor<span class="w"> </span>nano +</pre></div><p>Dál pokračuj obecným nastavením:</p> +<p><a id="config"></a></p> +<h2 id="nastaveni">Nastavení +<a href="#nastaveni" class="header-link">#</a> +</h2> +<p>Na projektu, který bude uložen v Gitu, může +spolupracovat více lidí. +Aby šlo dohledat, kdo udělal kterou změnu, je Gitu +potřeba říct jméno a e-mail. +Do příkazové řádky zadej následující příkazy, změň v nich ale +jméno a adresu:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>config<span class="w"> </span>--global<span class="w"> </span>user.name<span class="w"> </span><span class="s2">"Adéla Novotná"</span> +<span class="gp">$ </span>git<span class="w"> </span>config<span class="w"> </span>--global<span class="w"> </span>user.email<span class="w"> </span>adela.novotna@example.cz +</pre></div><p>Můžeš samozřejmě použít i přezdívku, nebo dokonce +falešný e-mail, ale v takovém případě bude složitější se +zapojit do týmových projektů. +Každopádně, jméno i e-mail jdou kdykoli změnit +tím, že konfigurační příkazy zadáš znovu.</p> +<div class="admonition note"><p>Pokud se bojíš spamu, neboj: nezačneš ho dostávat víc +než při normálním používání e-mailu. +Adresa se zobrazí jen lidem, kteří si stáhnou projekt, +do kterého jsi přispíval/a. +Spammeři se většinou zaměřují na méně technicky zdatné +lidi, než jsou uživatelé Gitu. :)</p> +</div><p>Dále si můžeš nastavit barevné výpisy – pokud si tedy +(jako někteří autoři Gitu) nemyslíš, že příkazová +řádka má být černobílá:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>config<span class="w"> </span>--global<span class="w"> </span>color.ui<span class="w"> </span><span class="nb">true</span> +</pre></div><div class="admonition note"><p>Spuštění <code>git config</code> nevypíše žádnou hlášku, že se operace povedla. +To je normální; stejně se chová spousta dalších příkazů, např. <code>cd</code>.</p> +<p>Aktuální konfiguraci gitu si můžeš zkontrolovat příkazem:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>git<span class="w"> </span>config<span class="w"> </span>--global<span class="w"> </span>--list +<span class="go">user.name=Adéla Novotná</span> +<span class="go">user.email=adela.novotna@example.cz</span> +</pre></div></div><p>A to je vše! Git máš nainstalovaný. Gratuluji!</p> \ No newline at end of file diff --git a/lessons/install/index.html b/lessons/install/index.html new file mode 100644 index 00000000..c238b86c --- /dev/null +++ b/lessons/install/index.html @@ -0,0 +1,14 @@ +<h1 id="instalace_pythonu">Instalace Pythonu +<a href="#instalace_pythonu" class="header-link">#</a> +</h1> +<p>V této sekci nainstalujeme Python.</p> +<p>To se na každém druhu operačního systému dělá trošku jinak. +Vyber si stránku podle svého systému:</p> +<ul> +<li><a href="naucse:page?lesson=beginners/install&page=linux">Linux</a></li> +<li><a href="naucse:page?lesson=beginners/install&page=windows">Windows</a></li> +<li><a href="naucse:page?lesson=beginners/install&page=macos">macOS</a></li> +</ul> +<p>Pokud máš jiný systém než Linux, Windows nebo macOS, +nebo pokud ke svému počítači neznáš administrátorské heslo, +napiš nám prosím e-mail.</p> \ No newline at end of file diff --git a/lessons/install/linux.html b/lessons/install/linux.html new file mode 100644 index 00000000..846f26d4 --- /dev/null +++ b/lessons/install/linux.html @@ -0,0 +1,81 @@ +<h1 id="instalace_pythonu_na_linux">Instalace Pythonu na Linux +<a href="#instalace_pythonu_na_linux" class="header-link">#</a> +</h1> +<p>Nainstalovat Pyhon na Linux je většinou jednoduché. +Jen existuje spousta druhů Linuxu a máme s ním největší zkušenosti, +tak jsou tyhle instrukce trochu delší. +Nezalekni se – většinu sekcí pravděpodobně přeskočíš. :)</p> +<h2 id="instalace_pythonu_3">Instalace Pythonu 3 +<a href="#instalace_pythonu_3" class="header-link">#</a> +</h2> +<p>Na Linuxu většinou Python 3 už bývá. Abys to zkontroloval/a, spusť +v <a href="naucse:page?lesson=beginners/cmdline">příkazové řádce</a> příkaz:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>python3<span class="w"> </span>--version +</pre></div><p>Objeví-li se „Python“ a číslo verze (např. <code>Python 3.6.6</code>) +a verze je 3.6 nebo vyšší, máš nainstalováno. +Přejdi na další sekci, <a href="#check-tkinter">kontrolu <code>tkinter</code></a>.</p> +<p>Objeví-li se „Python“ a verze 3.5 nebo nižší, +aktualizuj systém (nebo se poraď s někým, kdo to umí) a zkus to znovu.</p> +<p>Objeví-li se <code>bash: python3: command not found</code> nebo podobná chyba, +doinstaluj Python. +Konkrétní příkaz záleží na distribuci:</p> +<ul> +<li><strong>Fedora</strong>:<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>dnf<span class="w"> </span>install<span class="w"> </span>python3 +</pre></div> + +</li> +<li><strong>Ubuntu</strong>:<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>python3 +</pre></div> + +</li> +</ul> +<p>Používáš-li jinou distribuci, doufám, že instalovat programy už umíš.</p> +<p><a id="check-tkinter"></a></p> +<h2 id="kontrola_tkinter">Kontrola Tkinter +<a href="#kontrola_tkinter" class="header-link">#</a> +</h2> +<p>Některé linuxové distribuce obsahují standardně jen část celkové funkčnosti +Pythonu. +Konkrétně knihovnu <code>tkinter</code> (která umožňuje např. kreslit „želví obrázky“) +často musíme nainstalovat zvlášť. +Abys zjistil/a, jestli je už je nainstalovaná, zadej příkaz:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>python3<span class="w"> </span>-m<span class="w"> </span>tkinter +</pre></div><p>Objeví-li se okýnko, je všechno v pořádku. +Zavři ho a přejdi na <a href="#install-virtualenv">doinstalování <code>virtualenv</code></a>.</p> +<p>Jestli ne, modul <code>tkinter</code> ještě nainstaluj:</p> +<ul> +<li><strong>Fedora</strong>:<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>dnf<span class="w"> </span>install<span class="w"> </span>python3-tkinter +</pre></div> + +</li> +<li><strong>Ubuntu</strong>:<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>python3-tk +</pre></div> + +</li> +</ul> +<p>Používáš-li jinou distribuci, musíš si správné jméno balíčku najít na Internetu.</p> +<p><a id="install-virtualenv"></a></p> +<h2 id="doinstalovani_virtualenv">Doinstalování Virtualenv +<a href="#doinstalovani_virtualenv" class="header-link">#</a> +</h2> +<p>Novější verze Pythonu mají zabudovaný nástroj <code>venv</code>, který použijeme níže. +Starší verze ho ale nemají (a některé distribuce Linuxu ho dokonce z Pythonu +vyřadily). +Potřebuješ proto zjistit, jestli <code>venv</code> máš, a případně nainstalovat alternativu.</p> +<p>Spusť v příkazové řádce příkaz:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>python3<span class="w"> </span>-m<span class="w"> </span>ensurepip<span class="w"> </span>--version +</pre></div><p>Objeví-li se výpis začínající „pip“, máš funkční <code>venv</code> nainstalovaný. +Zbytek této sekce můžeš přeskočit!</p> +<p>Objeví-li se ale nápis <code>No module named ensurepip</code>, je potřeba doinstalovat +alternativu, Virtualenv:</p> +<!-- na Fedoře se tohle nestává --> + +<ul> +<li><strong>Ubuntu</strong>:<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>python3-virtualenv +</pre></div> + +</li> +</ul> +<p>Používáš-li jinou distribuci, doufám, že instalovat programy už umíš.</p> +<p>Instaluješ-li Virtualenv, <strong>zapamatuj si</strong>, že ho budeš muset použít později +při vytváření virtuálního prostředí.</p> \ No newline at end of file diff --git a/lessons/install/macos.html b/lessons/install/macos.html new file mode 100644 index 00000000..428a02ef --- /dev/null +++ b/lessons/install/macos.html @@ -0,0 +1,17 @@ +<h1 id="instalace_pythonu_pro_macos">Instalace Pythonu pro macOS +<a href="#instalace_pythonu_pro_macos" class="header-link">#</a> +</h1> +<p>Nainstaluj si nástroj <a href="http://brew.sh">Homebrew</a>, který řeší a zjednodušuje +instalaci aplikací a knihoven, které budeme potřebovat pro programování. +Jak na to?</p> +<p>Spusť v <a href="naucse:page?lesson=beginners/cmdline">příkazové řádce</a> příkaz:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>/bin/bash<span class="w"> </span>-c<span class="w"> </span><span class="s2">"</span><span class="k">$(</span>curl<span class="w"> </span>-fsSL<span class="w"> </span>https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="k">)</span><span class="s2">"</span> +</pre></div><p>Pak zadej následující příkaz a Python bude nainstalovaný:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>brew<span class="w"> </span>install<span class="w"> </span>python3 +</pre></div><p>Zkontroluj si, že máš verzi 3.6 nebo vyšší:</p> +<div class="highlight"><pre><span></span><span class="gp">$ </span>python3<span class="w"> </span>--version +</pre></div><p>Objeví-li se „Python“ a číslo verze (např. <code>Python 3.6.6</code>) +a verze je 3.6 nebo vyšší, máš nainstalováno. +Jinak je něco špatně; +zkus instalaci znovu. +Když to nevyjde, poraď se s někým zkušenějším.</p> \ No newline at end of file diff --git a/lessons/install/windows.html b/lessons/install/windows.html new file mode 100644 index 00000000..59729749 --- /dev/null +++ b/lessons/install/windows.html @@ -0,0 +1,57 @@ +<h1 id="instalace_pythonu_pro_windows">Instalace Pythonu pro Windows +<a href="#instalace_pythonu_pro_windows" class="header-link">#</a> +</h1> +<h2 id="zjisti_jestli_uz_mas_python_nainstalovany">Zjisti, jestli už máš Python nainstalovaný +<a href="#zjisti_jestli_uz_mas_python_nainstalovany" class="header-link">#</a> +</h2> +<p>Otevři si příkazovou řádku (pokud nevíš o co jde, přečti si o ní <a href="naucse:page?lesson=beginners/cmdline">tady</a>).</p> +<p>Napiš <code>python</code> a zmáčkni na klávesnici <kbd>Enter</kbd>. +Teď se může stát několik různých věcí:</p> +<ol> +<li>Otevře se ti Microsoft Store → okno zavři a pokračuj v kroku <a href="#download">Stažení</a></li> +<li>Zobrazí se ti text podobný tomuto níže?</li> +</ol> +<div class="highlight"><pre><code>> python +Python 3.8.1 (...) +Type "help", "copyright", "credits" or "license" for more information. +>>> +</code></pre></div><p>To je dobrá zpráva! Ještě si zkontroluj, jakou verzi máš nainstalovanou +(např.<code>Python 3.11.2</code> je <code>3.11</code>, <code>Python 3.8.1</code> je <code>3.8</code> atd.). +Třetí číslo za tečkou není tady podstatné.</p> +<ul> +<li>Je verze <code>3.6</code> nebo novější? Výborně, máš instalaci hotovou! +Okénko s příkazovou řádkou můžeš zavřít. +Až ho budeš znovu potřebovat, můžeš otevřít nové. +Pokračuj dále <a href="naucse:page?lesson=beginners/venv-setup">Nastavením prostředí</a>. +V opačném případě přejdi na <a href="#download">Stažení</a></li> +</ul> +<p><a id="download"></a></p> +<h2 id="stazeni">Stažení +<a href="#stazeni" class="header-link">#</a> +</h2> +<ul> +<li>Běž na <a href="https://www.python.org/downloads/windows/">stahovací stránku</a></li> +<li>klikni na <em>Latest Python 3 Release</em></li> +<li>v části <em>Files</em>, ve sloupci <em>Version</em>, vyber vhodný <em>Windows installer</em> pro tvou verzi Windows (32 nebo 64bit).</li> +</ul> +<div class="admonition note"><p>Kde zjistíš, zda máš 32bitové nebo 64bitové Windows? Otevři nabídku +<strong>Start</strong>, vyhledat „Systém“ a otevřít <strong>Systémové informace</strong>. +Pokud máš novější počítač, téměř jistě budeš mít Windows 64bitové.</p> +<p><span class="figure"><a href="naucse:static?filename=windows_32v64-bit.png"><img src="naucse:static?filename=windows_32v64-bit.png" alt="Screenshot zjišťování verze systému"></a></span></p> +</div><h2 id="instalace">Instalace +<a href="#instalace" class="header-link">#</a> +</h2> +<p>Stažený instalátor spusť.</p> +<p>Na začátku instalace zaškrtni:</p> +<ul> +<li><strong>Use admin privileges when installing py.exe</strong></li> +<li>a také <strong>Add python.exe to PATH</strong>. +Tyto volby ti zjednoduší vytvoření virtuálního prostředí.</li> +</ul> +<p>(Jestli nemáš administrátorské oprávnění, volbu +<em>Use admin privileges when installing py.exe</em> nezaškrtávej.)</p> +<p><span class="figure"><a href="naucse:static?filename=windows_add_python_to_path.png"><img src="naucse:static?filename=windows_add_python_to_path.png" alt="Screenshot instalace Pythonu"></a></span></p> +<p>Pak zmáčkni <strong>Install now</strong> a dále se drž instrukcí.</p> +<p>Máš-li otevřenou příkazovou řádku, po instalaci Pythonu ji zavři a otevři +novou. +Instalace mění systémové nastavení, které se musí načíst znovu.</p> \ No newline at end of file diff --git a/lessons/beginners/install/static/windows_32v64-bit.png b/lessons/install/windows_32v64-bit.png similarity index 100% rename from lessons/beginners/install/static/windows_32v64-bit.png rename to lessons/install/windows_32v64-bit.png diff --git a/lessons/beginners/install/static/windows_add_python_to_path.png b/lessons/install/windows_add_python_to_path.png similarity index 100% rename from lessons/beginners/install/static/windows_add_python_to_path.png rename to lessons/install/windows_add_python_to_path.png diff --git a/lessons/installing-naucse/index.html b/lessons/installing-naucse/index.html new file mode 100644 index 00000000..05eb99a5 --- /dev/null +++ b/lessons/installing-naucse/index.html @@ -0,0 +1 @@ +<p>Instalační instrukce teď najdeš v návodu na <a href="naucse:page?lesson=meta/local-run">Vytvoření lokálního kurzu</a>.</p> \ No newline at end of file diff --git a/lessons/interfaces/index.html b/lessons/interfaces/index.html new file mode 100644 index 00000000..b9616435 --- /dev/null +++ b/lessons/interfaces/index.html @@ -0,0 +1,83 @@ +<h1 id="rozhrani">Rozhraní +<a href="#rozhrani" class="header-link">#</a> +</h1> +<p>Už víš, že funkce ti umožňují kousek kódu:</p> +<ul> +<li>použít (zavolat) na více místech v programu, i když definice je jen jedna,</li> +<li>vyčlenit, aby detail (jako načtení čísla od uživatele) „nezavazel“ ve větším +programu, který tak může být přehlednější, a</li> +<li>pojmenovat, aby bylo jasné, co kód dělá, i bez toho, abys musel/a číst +samotné tělo funkce.</li> +</ul> +<p>Další výhoda funkce je, že ji můžeš jednoduše vyměnit za jinou, +lepší funkci – pokud má ta lepší funkce stejné <em>rozhraní</em> (angl. <em>interface</em>).</p> +<p>Aby se ti líp představovalo, o čem budeme povídat, představ si elektrickou +zásuvku ve zdi. +Do takové zásuvky můžeš zapojit počítač, lampu, nabíječku na mobil, vysavač, +nebo rádio. +Zásuvka poskytuje elektrický proud; je jedno, jak ho použiješ. +Stejně tak je jedno, jestli je „druhý konec“ zásuvky připojený k solárnímu +panelu nebo k atomové elektrárně. +Zásuvka poskytuje elektrický proud, a jsou u ní důležité určité parametry +(tvar, napětí, frekvence, maximální proud), na kterých se obě strany, +poskytovatel proudu i spotřebič, shodly. +Tyhle parametry tvoří <em>rozhraní</em>, které umožňuje připojit jakýkoli spotřebič +k jakékoli elektrárně.</p> +<h2 id="rozhrani_funkce">Rozhraní funkce +<a href="#rozhrani_funkce" class="header-link">#</a> +</h2> +<p>Podívej se na tuhle hlavičku funkce. +Je z ní poznat, co ta funkce dělá a jak ji použít?</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">ano_nebo_ne</span><span class="p">(</span><span class="n">otazka</span><span class="p">):</span> +<span class="w"> </span><span class="sd">"""Zeptá se uživatele na otázku a vrátí True nebo False dle odpovědi"""</span> + <span class="o">...</span> +</pre></div><p>Podobnou funkci už jsi napsal/a. +Když zavoláš <code>ano_nebo_ne('Chutná ti čokoláda?')</code>, otázka se objeví +na příkazové řádce. +Když uživatel odpoví, funkce vrátí <code>True</code> nebo <code>False</code>.</p> +<p>Co kdybys ale měl/a následující funkci?</p> +<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">ano_nebo_ne</span><span class="p">(</span><span class="n">otazka</span><span class="p">):</span> +<span class="w"> </span><span class="sd">"""Ukáže tlačítka "Ano" a "Ne" a až uživatel jedno zmáčkne, vrátí True</span> +<span class="sd"> nebo False dle stisknutého tlačítka."""</span> + <span class="o">...</span> +</pre></div><img src="naucse:static?filename=yn.png" alt="Screenshot s tlačítky Ano a Ne" style="display:block;float:right;"> + +<p>Když zavoláš tuhle funkci, <code>ano_nebo_ne('Chutná ti čokoláda?')</code>, ukáže se +okýnko se dvěma tlačítky. +Když uživatel jedno zmáčkne, funkce vrátí <code>True</code> nebo <code>False</code>.</p> +<p>Z hlediska programu se nic nemění: jediné, co se změní, je <em>definice funkce</em>; +volání a práce s návratovou hodnotou je pak stejné jako dřív.</p> +<h3 id="vyzkousej_si_to">Vyzkoušej si to! +<a href="#vyzkousej_si_to" class="header-link">#</a> +</h3> +<p>Najdi nějaký svůj program, který používá <code>ano_nebo_ne</code>, případně jen <code>print</code> +a <code>input</code>.</p> +<p>Stáhni si modul <a href="naucse:static?filename=tkui.py"><code>tkui.py</code></a> +do adresáře se svým programem. +Naimportuj z něho funkce, které potřebuješ. +Jsou k dispozici čtyři:</p> +<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">tkui</span> <span class="kn">import</span> <span class="nb">input</span><span class="p">,</span> <span class="n">nacti_cislo</span><span class="p">,</span> <span class="n">ano_nebo_ne</span><span class="p">,</span> <span class="nb">print</span> +</pre></div><p>Tento import <em>přepíše</em> vestavěné funkce <code>input</code> a <code>print</code> variantami, +které mají (téměř) stejné rozhraní – jen dělají něco trochu jinak.</p> +<p>Případné vlastní definice funkcí <code>nacti_cislo</code> a <code>ano_nebo_ne</code> pak z programu +vyndej, aby se použily ty naimportované.</p> +<div class="admonition note"><p>Funkce <code>tkui.nacti_cislo</code> potřebuje Python verze 3.7 nebo vyšší. +Používáš-li Python 3.6, <code>nacti_cislo</code> nenahrazuj.</p> +</div><p>Program by měl fungovat stejně jako dřív!</p> +<p>Je to tím, že tyto funkce mají stejné <em>rozhraní</em> jako jejich dřívější protějšky. +Rozhraní funkce tvoří všechno, co potřebuje kód, který funkce volá:</p> +<ul> +<li>jméno, kterým se funkce volá,</li> +<li>argumenty, které bere (např. <code>input</code> bere otázku jako řetězec; <code>print</code> +může brát více argumentů k vypsání), a</li> +<li>návratová hodnota, se kterou program pracuje dál (např. <code>input</code> vrací +řetězec; <code>print</code> nevrací nic smysluplného).</li> +</ul> +<p>Nékteré z těchto informací musíš do hlavičky funkce napsat vždy. +Ty ostatní je dobré popsat v dokumentačním řetězci, aby ten, kdo chce funkci +použít, věděl jak na to.</p> +<div class="admonition note"><p>Modul <code>tkui</code> je jen ilustrační. Je udělaný tak, aby se dobře “instaloval” +spíš než aby ti pomohl psát reálné programy. +V tomto kurzu se vrátíme zpět k příkazové řádce, která je dělaná tak, +aby byla užitečná pro programátory.</p> +</div> \ No newline at end of file diff --git a/lessons/beginners/interfaces/static/tkui.py b/lessons/interfaces/tkui.py similarity index 100% rename from lessons/beginners/interfaces/static/tkui.py rename to lessons/interfaces/tkui.py diff --git a/lessons/beginners/interfaces/static/yn.png b/lessons/interfaces/yn.png similarity index 100% rename from lessons/beginners/interfaces/static/yn.png rename to lessons/interfaces/yn.png diff --git a/lessons/intro/async/index.md b/lessons/intro/async/index.md deleted file mode 100644 index b65766c9..00000000 --- a/lessons/intro/async/index.md +++ /dev/null @@ -1,546 +0,0 @@ - -Ve cvičení použijeme ukázku z PyQt5. -Máte-li ještě virtualenv s nainstalovaným PyQt, použijte ho, případně ho -podle [lekce o PyQt] nainstalujte znovu. - -K PyQt si přiinstalujte knihovnu `asyncqt`: - -```console -$ python -m pip install asyncqt -``` - -Nejde-li to, nevadí – nezbytné dnes PyQt nebude. - -[lekce o PyQt]: {{ lesson_url('intro/pyqt') }} - - -Navíc si nainstalujte knihovnu `aiohttp`: - -```console -$ python -m pip install aiohttp -``` - -{% if not var('coach-present') %} ---- - -> [note] -> V minulosti byly na této stránce popsány i [generátory](../../advanced/generators/). -> Neovládáte-li je ještě, přečtěte si o nich. -{% endif %} - ---- - - -AsyncIO -======= - -Pojďme si povídat o souběžnosti – možnostech, jak nechat počítač dělat víc -úloh věcí najednou. - -Jak jsme si řekli v [lekci o C API](../cython/), Python má globální zámek, takže pythonní kód -může běžet jen v jednom vlákně najednou. -Taky jsme si řekli, že to většinou příliš nevadí: typický síťový nebo GUI program -stráví hodně času čekáním na události (odpověď z internetu, kliknutí myší atp.) -a u tohoto čekání není potřeba držet zámek zamčený. - -Servery typicky při zpracovávání požadavku stráví *většinu* času síťovou komunikací. -Proto se často spouští několik vláken nebo přímo procesů najednou, aby se mohl vytížit -procesor. -Při velkém množství vláken ale nastanou dva problémy. -První je, že vláken nemůže být neomezeně mnoho. -Každé vlákno potřebuje vlastní *stack*, tj. poměrně velkou část paměti; a počet vláken -bývá omezen i jinak (na Linuxu je globální limit počtu procesů, do kterého se počítají -i jednotlivá vlákna – viz `cat /proc/sys/kernel/threads-max`). -Druhý problém je, že přepnutí z jednoho vlákna do druhého se může stát *kdykoli*. -Ověřit si, že je na to program připravený, je poměrně složité a na zajištění -správné funkčnosti je potřeba zamykání či jiné techniky. Ty bývají relativně -pomalé, a tak se jim programátoři snaží vyhnout. -A chyby vzniklé nesprávným ošetřením přepínání vláken bývají složité na odhalení -a vyřešení. - -Vlákna jsou příklad *preemptivního multitaskingu*, kdy operační systém rozhoduje, -kdy přepne z jednoho vlákna do druhého, a tuto změnu si prakticky vynutí. -Jednotlivá vlákna se s tím musí vyrovnat. -Alternativou je *kooperativní multitasking*, kdy se jednotlivé úlohy umí *samy* vzdát -procesorového času, když např. čekají na síťovou komunikaci. -Programátor tak ví, že dokud takto nepředá kontrolu ostatním úlohám, žádná jiná -úloha mu pod rukama nemůže měnit stav procesu. -Na druhou stranu je ale potřeba dostatečně často kontrolu předávat, aby se všechny -úlohy dostaly ke slovu. -Tuto techniku tak nemůže používat operační systém, pod kterým můžou běžet i špatně -napsané programy. V rámci jednoho procesu se to ale dá s úspěchem využít. - - -Souběžnost v Pythonu --------------------- - -V Pythonu existovala a existuje řada knihoven, které nám umožňují „dělat více -věcí zároveň“. -Pro preemptivní multitasking jsou tu `threading`, tedy podpora pro vlákna, -a `multiprocessing`, tedy způsob jak spustit nový pythonní proces, -ve kterém se provede určitá funkce -(přičemž vstup a výstup se předává serializovaný přes *pipes*). - -Další knihovna, kterou lze z PyPI nainstalovat, je [greenlet]. -Ta nám dává k dispozici tzv. *mikro-vlákna*, -která se mezi sebou přepínají v rámci jednoho procesu. -Na rozdíl od systémových vláken nepotřebují tolik paměti navíc, ale -stále jde (alespoň z pohledu programátora) o *preemptivní* strategii: -k přepnutí může dojít kdykoli, -je tedy potřeba zamykat a složitě hledat málo časté chyby. - -Byly vyvinuty i knihovny pro *kooperativní* přepínání, založené na tzv. -*futures*. -Nejznámější jsou [Twisted] a [Tornado]. -Obě jsou relativně staré (2002, resp. 2009), ale stále populární. - -Ačkoli byly Twisted, Tornado a podobné knihovny užitečné, měly zásadní problém -v tom, že každá má jiné API. -Vznikaly tak kolem nich ekosystémy vázané na konkrétní knihovnu: -server napsaný pro Tornado se nedal použít pod Twisted a aplikace -využívající Twisted nemohla využít knihovnu pro Tornado. - -Jak to vyřešit? - - -Jeden standard --------------- - -![xkcd 927](https://imgs.xkcd.com/comics/standards.png) - -*Komiks [xkcd](https://xkcd.com/927/), © Randall Munroe, [CC-BY-NC](https://creativecommons.org/licenses/by-nc/2.5/)* - -Podobně jako přístup k různým SQL databázím je v Pythonu standardizovaný -(knihovny pro SQLite, Postgres, MySQL atd. všechny podporují API definované -v [PEP 249]) nebo je standardizované API webových serverů (WSGI, [PEP 3333]), -tak vzniklo standardizované API pro kooperativní multitasking. -Toto API je definováno v [PEP 3156] a jeho referenční implementace, `asyncio`, -je ve standardní knihovně Pythonu. -(Ne že by to zabránilo vzniku dalších asynchronních knihoven jako -Curio a [Trio](https://trio.readthedocs.io/en/stable/tutorial.html). -Ty ovšem spíš experimentují s novými paradigmaty a osvědčené principy se z nich -postupně dostávají do `asyncio`.) - -Interně je `asyncio` postavené na konceptu *futures* inspirovaných Tornado/Twisted, -ale jeho „hlavní“ API je postavené na *coroutines* podobných generátorům. - -Od Pythonu verze 3.5 používá `asyncio` místo „normálních“ generátorů -speciální syntaxi, která umožňuje kombinovat asynchronní funkce s příkazy -`for` a `with` nebo i `yield`. -Tuto syntaxi použijeme i tady; máte-li starší Python, podívejte se na potřebné změny uvedené níže. - -Jak vypadá taková asynchronní funkce? -Definuje se pomocí `async def` místo `def`, a může používat příkaz `await`. - -Ukažme si to na příkladu: - -```python -import asyncio - -async def count(name, interval): - """Prints numbers from 0 in regular intervals""" - i = 0 - while True: - print(name, 'counts', i) - await asyncio.sleep(interval) - i += 1 - - -loop = asyncio.get_event_loop() -loop.run_until_complete(count('Counter', 1)) -loop.close() -``` - -Co se tu děje? -Příkazem `await asyncio.sleep(interval)` se asynchronní funkce pozastaví -(podobně jako generátor při `yield`) a předá kontrolu knihovně `asyncio` -s informací že za daný čas by kontrolu chtěla zase zpátky. -Než daný interval uplyne, `asyncio` může spouštět jiné úlohy; -po jeho uplynutí pozastavenou funkci „probudí“ a její algoritmus pokračuje dál. - -Když žádné jiné úlohy neexistují, je pomalé počítání trochu nudné. -Zkuste proto spustit počítadla dvě. -(Detaily funkce `ensure_future` a příkazu `await task` vysvětlíme níže.) - -```python -import asyncio - -async def count(name, interval): - ... - -async def run_two_counters(): - fast_task = asyncio.ensure_future(count('Fast', 0.3)) - slow_task = asyncio.ensure_future(count('Slow', 1)) - await fast_task - await slow_task - -loop = asyncio.get_event_loop() -loop.run_until_complete(run_two_counters()) -loop.close() -``` - -Spouštění a ukončení – poslední tři řádky – je poněkud složité na zápis, -ale typicky to v každém programu potřebujete napsat jen jednou. - -> [note] -> V Pythonu verze 3.7 a vyšší lze ty tři poslední řádky nahradit jednodušším: -> -> ```python -> asyncio.run(run_two_counters()) -> ``` - -> [note] -> V Pythonu verze 3.4 a nižší ještě neexistovala klíčová slova `async` a -> `await`; asynchronní funkce byly implementovány jako generátory. -> Ve starších verzích Pythonu bylo potřeba místo: -> -> ```python -> async def ...: -> await ... -> ``` -> -> psát: -> -> ```python -> @asyncio.coroutine -> def ...: -> yield from ... -> ``` -> -> Starý způsob zatím funguje i v novějším Pythonu a dokonce se ještě někdy -> objeví i v dokumentaci. -> Od Pythonu 3.8 je ale *deprecated*. - -[greenlet]: https://greenlet.readthedocs.io/en/latest/ -[Tornado]: http://www.tornadoweb.org/en/stable/ -[Twisted]: https://twistedmatrix.com/trac/ -[PEP 249]: https://www.python.org/dev/peps/pep-0249/ -[PEP 3333]: https://www.python.org/dev/peps/pep-3333/ -[PEP 3156]: https://www.python.org/dev/peps/pep-3156/ -[pypi-asyncio]: https://pypi.org/project/asyncio/ - - -Event Loop ----------- - -Knihovna `asyncio` nám dává k dispozici *smyčku událostí*, která se, podobně jako -`app.exec` v Qt, stará o plánování jednotlivých úloh. -Smyček událostí může být více. -Tradiční způsob je, že každé vlákno může mít vlastní smyčku událostí, -kterou získáme pomocí `asyncio.get_event_loop` a pak ji můžeme spustit dvěma -způsoby: - -* `loop.run_forever()` spustí smyčku na tak dlouho, dokud jsou nějaké úlohy - naplánovány (to trochu odporuje názvu, ale většinou se nestává, že by se - úlohy „vyčerpaly“), nebo -* `loop.run_until_complete(task)` – tahle funkce skončí hned, jakmile je hotová - daná úloha, a vrátí její výsledek. - -Nakonec je smyčku potřeba uzavřít (`loop.close()`), což např. dá použitým -knihovnám možnost korektně uzavřít zbylá síťová spojení. - -Od Pythonu 3.7 můžete použít `asyncio.run(task)`, což vytvoří *novou* -smyčku událostí, spustí v ní danou úlohu (pomocí `run_until_complete`) -a zase ji zavře. -Chcete-li ji použít (a tedy psát kód jen pro Python 3.7+), používejte pak všude -místo `ensure_future` funkci `create_task`, která vás lépe ochrání před -těžko nalezitelnými chybami. - - -Async funkce a Task -------------------- - -Smyčka událostí provádí úlohy a asynchronní funkce. - -Asynchronní funkce se definují pomocí `async def` a umožňují použít příkaz -(nebo operátor) `await`, kterým se provádění funkce pozastaví a kontrola se -předá jiným úlohám do doby, než nastane nějaká událost (např. uplynul časový -intervaluj, jsou dostupná nová data ze socketu…). - -Pozastavení funguje podobně jako `yield` u generátoru. - -Zavoláním asynchronní funkce dostaneme *coroutine* pozastavenou na začátku -těla funkce: - -```pycon ->>> async def demo(): -... print('Demo') -... ->>> coroutine = demo() ->>> coroutine -<coroutine object demo at 0x7fda8be22b90> -``` - -Naplánujeme-li provádění *coroutine* na smyčce událostí -(např. pomocí `run_until_complete`), tělo funkce se začne vykonávat: - -```pycon ->>> loop = asyncio.get_event_loop() ->>> result = loop.run_until_complete(coroutine) -Demo -``` - -V rámci jedné *coroutine* pak lze provedení jiné *coroutine* naplánovat -a počkat na jejich skončení pomocí `await`. -Jak `run_until_complete` tak `await` nám dají k dispozici návratovou hodnotu -příslušné asynchronní funkce. - -```python -import asyncio - -async def add(a, b): - await asyncio.sleep(1) # schedule a "sleep" and wait for it to finish - return a + b - -async def demo(): - coroutine = add(2, 3) - result = await coroutine # schedule "add" and wait for it to finish - print('The result is:', result) - -loop = asyncio.get_event_loop() -result = loop.run_until_complete(demo()) -loop.close() -``` - -Nevýhoda čistých *coroutines* spočívá v tom, že na každé zavolání -takové funkce lze použít jen jeden `await`. -Výsledek se nikam neukládá, jen se po skončení jednou předá. -Druhý `await` pro stejné zavolání asynchronní funkce skončí s chybou. -Zkuste si to – v kódu výše přidejte daší řádek s `await coroutine`: - -```python - print('The result is:', (await coroutine)) -``` - -Tenhle problém můžeme vyřešit tak, že asynchronní funkci „zabalíme“ -jako úlohu, *Task*. -V Pythonu 3.7 se Task tvoří pomocí `asyncio.create_task`; -pro kompatibilitu se staršími verzemi ale použijeme ekvivalentní -`asyncio.ensure_future`. -Task se chová stejně jako *coroutine* – lze použít v `await` nebo -`run_until_complete`, ale navíc: - -* výsledek je k dispozici kdykoli po ukončení funkce (např. pro druhý `await`) a -* úloha se naplánuje hned po zavolání `ensure_future`. - -Druhou vlastnost je lepší ukázat na příkladu: - -```python -import asyncio - -async def print_and_wait(): - print('Async function starting') - await asyncio.sleep(0.5) - print('Async function done') - return 'result' - -async def demo_coro(): - coroutine = print_and_wait() - await asyncio.sleep(1) - print('Awaiting coroutine') - print(await coroutine) # schedule coroutine and wait for it to finish - -async def demo_task(): - task = asyncio.ensure_future(print_and_wait()) # schedule the task - await asyncio.sleep(1) - print('Awaiting task') - print(await task) # task is finished at this point; retreive its result - - -loop = asyncio.get_event_loop() -print('Coroutine:') -result = loop.run_until_complete(demo_coro()) -print('Task:') -result = loop.run_until_complete(demo_task()) -loop.close() -``` - -Fan-Out a Fan-In ----------------- - -S pomocí asynchronních funkcí můžeme nad našimi programy přemýšlet tak, -jako by to byly „normální“ procedurálně zapsané algoritmy: máme jedno -„vlákno“, které se provádí od začátku do konce, jen na některých místech -(označených `await`) se provádění přeruší a zatímco náš kód čeká na výsledek -nějaké operace, může se spustit jiný kus kódu. -Funkce, na které je takto potřeba čekat, bývají v dokumentaci patřičně -označeny (v síťovém programování je to většinou čtení ze socketů nebo inicializace -či ukončení serveru). - -Pomocí `ensure_future` a `await` můžeme k tomu dělat něco navíc: -rozdělit běh našeho programu na víc úloh, které se budou vykonávat „souběžně“ – -například autor scraperu chce stáhnout několik stránek najednou -nebo server souběžně odpovídá na několik požadavků. -Tomuto rozdělení se říká *fan-out*. - -Opačná operace je *fan-in*, kdy několik úloh opět spojíme do jedné. -Výše uvedený scraper může počkat, než jsou všechny stránky stažené – -třeba pomocí jednoho `await` pro každý *Task* nebo asynchronní funkce -[gather](https://docs.python.org/3/library/asyncio-task.html#asyncio.gather). -Poté může pokračovat zpracováním získaných dat. - -Počkáním na úlohu (pomocí `await`, `gather`, `run_until_complete` atp.) -získáte její návratovou hodnotu. -Ale na všechny úlohy, i na ty, které nic zajímavého nevrací, je důležité počkat. -Neuděláte-li to, bude `asyncio` vypisovat varovné hlášky: - -```pycon ->>> import asyncio ->>> asyncio.sleep(1) # no await ->>> exit() -sys:1: RuntimeWarning: coroutine 'sleep' was never awaited -``` - -Toto varování nikdy neignorujte. Kdyby váš program nedělal co má, spolu s -varováním byste ignorovali výjimky v této *coroutine*. - - -Asynchronní cykly a kontexty ----------------------------- - -Až budete používat některé „asynchronní“ knihovny, setkáte se pravděpodobně se dvěma -novými konstrukcemi: `async for` a `async with`. - -Fungují jako jejich „ne-`async`“ varianty, jen na začátku a konci každé iterace (resp. -na začátku a konci bloku) můžou přerušit vykonávání funkce – podobně jako `await`. - -Typický příklad je u databází: začátek a konec transakce i získávání jednotlivých -řádků pravděpodobně potřebují komunikaci po síti, takže hypotetická databázová -knihovna by se mohla používat nějak takto: - -```python -async with database.transaction_context(): - await database.execute('UPDATE ...') - async for row in (await database.execute('SELECT ...')): - handle(row) -``` - - -A další -------- - -Nakonec několik tipů, o kterých je dobré vědět. - -V `asyncio` najdeme synchronizační mechanismy známé z vláknového programování, např. -`Lock` a `Semaphore` – viz [dokumentace](https://docs.python.org/3/library/asyncio-sync.html). - -Musíme-li použít blokující funkci, která např. komunikuje po síti bez `await` a která by -tedy zablokovala i všechny ostatní úlohy, můžeme použít -`loop.run_in_executor()`, a tím danou funkci zavolat ve vlákně nebo podprocesu, ale výsledek zpřístupnit -jako objekt, na který lze počkat pomocí `await`. -Použití je opět popsáno v [dokumentaci](https://docs.python.org/3/library/asyncio-eventloop.html#executor). - -Občas vás při programování s `asyncio` zaskočí zrádná chyba. -V takových případech je dobré zapnout *debug* režim pomocí proměnné prostředí `PYTHONASYNCIODEBUG=1`. -V tomto režimu `asyncio` upozorňuje na časté chyby, do některých chybových výpisů přidává informaci o tom, -kde aktuální `Task` vznikl, apod. -Více informací je zase v [dokumentaci](https://docs.python.org/3/library/asyncio-dev.html#asyncio-dev). - - -Alternativní smyčky událostí ----------------------------- - -Jak bylo zmíněno na začátku, hlavní cíl `asyncio` je definovat společné rozhraní -pro různé asynchronní knihovny, aby bylo možné např. kombinovat knihovny pro -Tornado se smyčkou událostí v Twisted. -Samotné `asyncio` je jen jedna z mnoha implementací tohoto rozhraní. -Zajímavá je například knihovna [uvloop], která je asi 2-4× rychlejší než `asyncio` -(ale má závislosti, které se pro součást standardní knihovny nehodí). - -Další zajímavá implementace je [asyncqt], která pod standardním `asyncio` API používá -smyčku událostí z Qt. -Umožňuje tak efektivně zpracovávat Qt události zároveň s asynchronními funkcemi -známými z `asyncio`. - -*Event loop* z `asyncqt` je potřeba na začátku programu naimportovat a nastavit -jako hlavní smyčku událostí, a poté ji, místo Qt-ovského `app.exec()`, spustit. -Jednotlivé asynchronní funkce se pak používají jako v čistém `asyncio`: -pomocí `asyncio.ensure_future`, `await`, atd. - -[uvloop]: https://pypi.org/project/uvloop/ -[asyncqt]: https://pypi.org/project/asyncqt/ - -Ukázka: - -```python -import asyncio - -from PyQt5 import QtGui, QtWidgets -from asyncqt import QEventLoop - -app = QtWidgets.QApplication([]) -loop = QEventLoop(app) -asyncio.set_event_loop(loop) - -display = QtWidgets.QLCDNumber() -display.setWindowTitle('Stopwatch') - -display.show() - -async def update_time(): - value = 0 - while True: - display.display(value) - await asyncio.sleep(1) - value += 1 - -asyncio.ensure_future(update_time()) - -loop.run_forever() -``` - - -Komunikace ----------- - -Ono `io` v `asyncio` naznačuje, že je tato knihovna dělaná především na -vstup a výstup – konkrétně na komunikaci přes síť (případně s jinými procesy). - -Ke komunikaci používá `asyncio` tři úrovně abstrakce: `Transport`, `Protocol` -a `Stream`. -V krátkosti si je tu popíšeme; detaily pak najdete v dokumentaci (je pro nás -totiž mnohem důležitější abyste pochopili principy, než abyste uměli konkrétní -API, které lze dohledat v dokumentaci). - -Transporty a protokoly jsou postaveny na konceptech knihovny `Twisted`. - -`Transport` zajišťuje samotné posílání bajtů mezi počítači (transportní vrstvu), kdežto -`Protocol` implementuje nějaký aplikační protokol. -`Transport` většinou nepíšeme sami, použijeme existující. -V `asyncio` jsou zabudované transporty pro TCP, UDP a SSL. -`Protocol` je pak použit pro implementaci konkrétních protokolů jako -`HTTP`, `FTP` a podobně. -V dokumentaci najdete podrobnější popis včetně [příkladů][transport-proto-examples]. - -[transport-proto-examples]: https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server-protocol - -Kromě toho existuje i „Stream API“ založené na asynchronních funkcích. -Většinou platí, že operace *otevření*, *čtení*, *flush* a *zavření* Streamu -jsou asynchronní funkce (v dokumentaci označované jako *coroutines*), a je -tedy nutné je použít s `await`; oproti tomu *zápis* asynchronní není – data -se uloží do bufferu a pošlou se, až to bude možné. - -Typicky ale místo čistého `asyncio` použijeme existující knihovnu. -Tady je příklad z knihovny `aiohttp`, která implementuje server a klienta -pro HTTP: - -```python -import asyncio -import aiohttp - -async def main(url): - # Use a a session - session = aiohttp.ClientSession() - async with session: - - # Get the response (acts somewhat like a file; needs to be closed) - async with session.get(url) as response: - - # Fetch the whole text - html = await response.text() - print(html) - -loop = asyncio.get_event_loop() -loop.run_until_complete(main('http://python.cz')) -loop.close() -``` diff --git a/lessons/intro/async/info.yml b/lessons/intro/async/info.yml deleted file mode 100644 index 05c5c587..00000000 --- a/lessons/intro/async/info.yml +++ /dev/null @@ -1,5 +0,0 @@ -course: MI-PYT -title: AsyncIO -style: md -attribution: Pro kurz MI-PYT na ČVUT napsali Petr Viktorin, Miro Hrončok a další, 2016-2017. -license: cc-by-sa-40 diff --git a/lessons/intro/click/index.md b/lessons/intro/click/index.md deleted file mode 100644 index 186c7a10..00000000 --- a/lessons/intro/click/index.md +++ /dev/null @@ -1,271 +0,0 @@ -click -===== - -{% if var('mi-pyt') %} -Nechme internety na chvíli být a pojďme se podívat na úplně jinou knihovnu, -[click](https://click.palletsprojects.com/en/7.x/). -{% endif %} - -Knihovna `click` slouží k vytváření rozhraní pro příkazovou řádku -(angl. *command line interface*, CLI). -Primárně to je zpracování argumentů, ale click umí zjednodušit i výstup. - -Click je dobré používat s knihovnou `colorama`, která se stará o obarvování -textu na příkazové řádce ve Windows (a na Unixu nedělá nic). -Nainstalujte si tedy obě: - -```console -$ python -m pip install click colorama -``` - - -## Argumenty příkazové řádky - -Nástroje na zpracování argumentů z CLI jsou i přímo ve standardní knihovně, -a dokonce jich není málo: [sys.argv], [argparse], [optparse], [getopt]. -S knihovnou `click` je ale práce mnohem příjemnější a výsledky většinou -lépe odpovídají zavedeným konvencím. - -> [note] -> Cena za jednoduchost a konzistenci je, že některé styly návrhu CLI click -> nepodporuje. -> Máte-li existující rozhraní, které chcete jen převést do Pythonu, -> click nejspíš nebude nejlepší volba. - -[sys.argv]: https://docs.python.org/3/library/sys.html#sys.argv -[argparse]: https://docs.python.org/3/library/argparse.html -[optparse]: https://docs.python.org/3/library/optparse.html -[getopt]: https://docs.python.org/3/library/getopt.html - -Takto jednoduše se dá vytvořit aplikace s přepínači: - -```python -import click - -@click.command() -@click.option('--count', default=1, metavar='COUNT', - help='Number of greetings.') -@click.option('--name', prompt='Your name', metavar='NAME', - help='The person to greet.') -def hello(count, name): - """Simple program that greets NAME for a total of COUNT times.""" - for x in range(count): - click.echo(f'Hello {name}!') - -if __name__ == '__main__': - hello() -``` - -Vyzkoušejte si ji! Máte-li ji uloženou jako `hello.py`, zkuste: - -```console -$ python hello.py -$ python hello.py --help -$ python hello.py --name Pythonista -$ python hello.py --count 5 -``` - - -## Příkazy a přepínače - -Funkce s dekorátorem `@click.command` je *příkaz* – když ji zavoláte, -click zpracuje argumenty příkazové řádky a zavolá původní funkci -s příslušnými pythonními hodnotami. -Proto se dává do bloku `if __name__ == '__main__':`, který se spustí, jen -když se pythonní soubor spoustí „přímo“. -Když je soubor importován, tenhle blok se neprovede. - -Dekorátory `@click.option` a `@click.argument` pak přidávají přepínače -a argumenty. - -[*Přepínače*](https://click.palletsprojects.com/en/7.x/options/) (angl. *options*), přidávané pomocí `option`, jsou nepovinné -parametry, kterými se nějak obměňuje chování programu. -Pokud uživatel nějaký přepínač nezadá, použije se hodnota zadaná jako `default` -(nebo `None`, když `default` chybí). - -Podle `default` se řídí i typ argumentu, není-li dán explicitně. -Takže `count` v příkladu výše je celé číslo. - -Jména přepínačů začínají, podle Unixové konvence, pomlčkami: jednou pomlčkou -pro jednopísmenné zkratky, dvěma pomlčkami pro vícepísmenná jména. -Jeden přepínač může mít i víc jmen. - -Speciální případ jsou booleovské přepínače, které mají jedno jméno -pro `True` a jiné pro `False`. Lze samozřejmě také vytvořit bezhodnotový -přepínač pomocí `is_flag`. - -```python -import click - -@click.command() -@click.option('-n', '--name', default='world', - help='Name of the person to greet') -@click.option('-c/-C', '--color/--no-color', - help='Make the output colorful') -@click.option('-v', '--verbose', is_flag=True, - help='More verbose output') -def hello(name, color, verbose): - if color: - name = click.style(name, fg='blue') - click.echo(f'Hello {name}!') - if verbose: - click.echo('Nice to meet you.') - -if __name__ == '__main__': - hello() -``` - -```console -$ python hello.py -Hello world! -$ python hello.py --name Guido -Hello Guido! -$ python hello.py --name Jane -v -Hello Jane! -Nice to meet you. -$ python hello.py -n 'Mr. Git' -Hello Mr. Git! -$ python hello.py --help -Usage: hello.py [OPTIONS] - -Options: - -n, --name TEXT Name of the person to greet - -c, --color / -C, --no-color Make the output colorful - --help Show this message and exit. -``` - -Přepínač `--help` přidává click sám. - - -## Argumenty - -Kromě přepínačů podporuje click i [*argumenty*](https://click.palletsprojects.com/en/7.x/arguments/). -Přepínače musí uživatel na řádce pojmenovat; argumenty se zadávají beze jména, -ale záleží u nich na pořadí. -Používají se ve dvou případech: pro povinné parametry a pro parametry, kterých -může být zadán libovolný počet. -Na všechno ostatní radši použijte přepínače. - -Například příkaz `cd` potřebuje jeden argument: jméno adresáře, -do kterého má přepnout. -Jeho rozhraní by v clicku vypadalo takto: - -```python -@click.command() -@click.argument('directory') -def cd(directory): - """Change the current directory""" - click.echo(f'Changing to directory {directory}') -``` - -Proměnný počet argumentů se zadává pomocí `nargs=-1` (0 nebo víc argumentů) -nebo `nargs=-1, required=True` (1 nebo víc). - -Například příkaz `mv` bere <var>N</var> souborů a adresář, kam je přesune. -Takové rozhraní by v clicku vypadalo následovně: - -```python -@click.command() -@click.argument('source', nargs=-1, required=True) -@click.argument('destination') -def mv(source, destination): - """Move any number of files to one destination""" - for filename in source: - click.echo(f'Moving {filename} to {destination}') -``` - - -## Soubory - -Má-li uživatel zadat jméno souboru, nepoužívejte řetězce, ale speciální typ -[`click.File()`](https://click.palletsprojects.com/en/7.x/api/#click.File). -Click za vás soubor automaticky otevře a zavře. -Kromě toho podporuje unixovskou konvenci, že `-` znamená standardní -vstup/výstup. - -Argument pro `File` je mód, ve kterém se soubor otevírá, podobně jako pro -funkci [`open`](https://docs.python.org/3/library/functions.html#open): -`'r'` pro čtení, `'w'` pro zápis. - -```python -@click.command() -@click.argument('files', nargs=-1, type=click.File('r')) -def cat(files): - """Print out the contents of the given files""" - for file in files: - print(file.read(), end='') -``` - -Existuje i varianta [`click.Path()`](https://click.palletsprojects.com/en/7.x/api/#click.Path), -která soubor neotvírá. Pomocí ní jde např. zadat jméno adresáře. Click takto -poskytuje i jiné [další typy](https://click.palletsprojects.com/en/7.x/api/#types). - - -## Validace vstupů - -Vstupy získané z přepínačů i argumentů lze ověřit pomocí -vlastních podmínek a podle toho naprogramovat chování včetně -chybových hlášek. Click však opět nabízí pohodlnější způsob, -a to pomocí [`callback`](https://click.palletsprojects.com/en/7.x/options/#callbacks-for-validation). -V rámci callback funkce můžete ověřit libovolně hodnotu a/nebo -ji vhodně transformovat. Pokud hodnota neodpovídá požadavkům, -můžete použít vyjímku [`click.UsageError`](https://click.palletsprojects.com/en/7.x/api/#click.UsageError) -nebo [`click.BadParameter`](https://click.palletsprojects.com/en/7.x/api/#click.BadParameter) -(vztahuje-li se přímo ke konkrétnímu parametru). Click se pak -sám postará o případné ukončení programu s odpovídající chybovou -hláškou a kódem. - -```python -def validate_username(ctx, param, value): - if 2 <= len(value) <= 8 and re.match('^[a-zA-Z]+[0-9]*$', value): - return value.lower() - else: - raise click.BadParameter('not valid CTU username') - -@click.command() -@click.option('-u', '--username', callback=validate_username) -def email(username): - click.echo(f'{username}@fit.cvut.cz') - -if __name__ == '__main__': - email() -``` - - -## Podpříkazy - -Click má dobrou podporu pro *podpříkazy* známé z verzovacích systémů jako git: -příkaz `git` sám o sobě nedělá nic, jen sdružuje podpříkazy jako `git add` -a `git commit`. - -Umí-li váš program více akcí, souhrnný příkaz označte `@click.group()` -a jednotlivé podpříkazy pak přidávejte pomocí `command()`: - -```python -@click.group() -def git2(): - pass - -@git2.command() -def commit(): - message = click.edit('Made some changes') - click.echo(f'Making commit with message: {message}') - -@git2.command() -@click.argument('files', nargs=-1) -def add(files): - for file in files: - click.echo(f'Adding {file}') -``` - - -## A další - -Tahle lekce není popis všeho, co click umí – je to jen ochutnávka, -abyste věděli, co od téhle knihovny očekávat. - -Click má velice dobrou [dokumentaci], ve které najdete detaily i všechny -ostatní možnosti. - -[dokumentaci]: https://click.palletsprojects.com/en/7.x/ - diff --git a/lessons/intro/click/info.yml b/lessons/intro/click/info.yml deleted file mode 100644 index bba12b2b..00000000 --- a/lessons/intro/click/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Click – Rozhraní pro příkazovou řádku -style: md -attribution: Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017. -license: cc-by-sa-40 diff --git a/lessons/intro/cython/index.md b/lessons/intro/cython/index.md deleted file mode 100644 index 98ec20e0..00000000 --- a/lessons/intro/cython/index.md +++ /dev/null @@ -1,868 +0,0 @@ -Dnes budeme potřebovat do virtuálního prostředí nainstalovat tyto knihovny: - -```console -$ python -m pip install --upgrade pip -$ python -m pip install notebook numpy cython pytest pytest-profiling -``` - -Také je potřeba nainstalovat překladač jazyka C -a hlavičkové soubory Pythonu: - -* Na Linuxu bude stačit nainstalovat balíčky `gcc` - a `python3-devel` (Fedora) nebo `python3-dev` (Ubuntu/Debian). -* Na Windows se řiďte instrukcemi pro vaši verzi Pythonu - na [Python wiki](https://wiki.python.org/moin/WindowsCompilers). - - ---- - -C API -===== - -Mluvíme-li o „Pythonu“, máme často na mysli jak jazyk samotný, tak i interpret, -program, který programy v tomto jazyce umí spouštět. -Správně je ale „Python“ pouze jméno jazyka. -Interpretů tohoto jazyka je více, například: - -* CPython, referenční implementace napsaná v C; interpret, který spouštíme příkazem `python3` -* PyPy, implementace zaměřená na rychlost, napsaná v Pythonu -* MicroPython, implementace pro mikroprocesory a zařízení s minimem paměti -* Jython, implementace napsaná v Javě, která umožňuje využívat javovské třídy -* IronPython, napsaný v C#, s integrací do .NET -* Batavia, Brython, pyjs – různé pokusy o integraci do JavaScriptu - -Jednotlivé interprety se liší v detailech jako jsou přesnost reálných čísel, -vypisování chybových hlášek, řazení záznamů ve slovnících nebo přístup -k interním strukturám interpretu. -Správně napsaný pythonní program by neměl na takových detailech záviset, pokud -není k nekompatibilitě mezi interprety dobrý důvod. - -Někdy to ale je potřeba, a dnešní přednáška bude specifická pro CPython -a přímé využití jeho API pro jazyk C. - - -Rychlost --------- - -Častý důvod proč sáhnout k C API je rychlost: CPython je celkem pomalý. -Tradiční metoda optimalizace je zjistit, které části jsou kritické, a přepsat -je do C. -Využijí se tak výhody obou jazyků: Python pro rychlý vývoj, snadné -prototypování a přehlednost kódu, a C pro rychlost. - -Když je náš program příliš pomalý, je potřeba ho optimalizovat. -První krok k tomu je vždy zkontrolovat, co zabírá více času, než by mělo. -K tomu se dá použít nástroj `profile` ze standardní knihovny, který vypíše -tabulku počtu volání jednotlivých funkcí a času v nich stráveného: - -```console -$ python -m profile -s cumtime program.py -``` - -Profilovat běh pytest testů se dá jednoduše pomocí modulu [pytest-profiling]: - -```console -$ python -m pip install pytest-profiling -$ python -m pytest --profile -``` - -[pytest-profiling]: https://pypi.python.org/pypi/pytest-profiling - -Když máme představu o tom, co nás brzdí, můžeme začít přepisovat do C způsoby -popsanými níže. - -Jiná možnost, jak program zrychlit, je ho pustit, tak jak je, pod interpretem -PyPy, který obsahuje optimalizovaný překladač. To je ale jiná kapitola. - - -Externí knihovny ----------------- - -Druhý důvod, proč programátoři používají C API, je použití knihoven, které mají -rozhraní pro C. -Takových knihoven existuje mnoho – pokud není něco specifické pro určitý jazyk, -často se to dá volat i z C. - -Pro práci s externími knihovnami se dá použít C API nebo vestavěný modul -[ctypes], ale v dnešní době je dobré místo toho použít [CFFI], knihovnu -která funguje i s PyPy (a teoreticky jinými implementacemi). - -[ctypes]: https://docs.python.org/3/library/ctypes.html -[CFFI]: http://cffi.readthedocs.io/en/latest/ - - -CPython -------- - -Třetí důvod, proč použít C API, je práce s CPythonem samotným. -Když člověk zabředne do složitého problému, může na CPython pustit C debugger -jako [gdb] nebo [Valgrind], prozkoumat potíže na nižší úrovni -a zjistit, kde přesně se chyba nachází. - -[gdb]: https://en.wikipedia.org/wiki/GNU_Debugger -[Valgrind]: http://valgrind.org/ - - -Modul v C ---------- - -Pojďme začít příkladem. -Vytvořte si následující soubor, který implementuje rozšíření -(importovatelný modul) s jednou funkcí. - -(Nebudeme chtít, abyste podobný kód uměli napsat, ale měli byste být schopní -porozumět tomu, co dělá.) - -demo.c: - -```c -#include <Python.h> - -PyDoc_STRVAR( - mod_docstring, - "Demo extension module with a Python wrapper for the system(3) function"); - -static PyObject *demo_system(PyObject *self, PyObject *args){ - const char *command; - int retval; - - /* Parse the given arguments: expect one string, convert to char* */ - if (!PyArg_ParseTuple(args, "s", &command)) { - /* Error handling: if PyArg_ParseTuple returns zero, return NULL */ - return NULL; - } - - /* Call the C function */ - retval = system(command); - - /* Return result as Python int (error handling built in) */ - return PyLong_FromLong(retval); -} - -/* List of all methods in the module */ -static PyMethodDef DemoMethods[] = { - {"system", demo_system, METH_VARARGS, - PyDoc_STR("Execute a shell command.")}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -/* Module specification */ -static struct PyModuleDef demo_module = { - PyModuleDef_HEAD_INIT, - "demo", /* name of module */ - mod_docstring, /* dosctring (may be NULL) */ - 0, /* size of per-interpreter state of the module */ - DemoMethods, /* list of methods */ -}; - - -/* Module entrypoint */ -PyMODINIT_FUNC -PyInit_demo(void) -{ - return PyModuleDef_Init(&demo_module); -} -``` - -Z tohoto souboru by měla být patrná struktura podobných rozšíření: -máme funkci (`demo_system`), která převádí objekty Pythonu -na datové typy C, volá samotnou funkci a výsledek převádí zpět na pythonní -objekt. - -Dále máme pole záznamů o funkcích (`DemoMethods`), kde je ke každé funkci -přiřazeno jméno, dokumentační řetězec a způsob volání (v našem případě -METH_VARARGS, tedy volání s proměnným počtem nepojmenovaných argumentů, -podobně jako bychom v Pythonu napsali `def system(*args)`). - -Další potřebná proměnná, `demo_module`, obsahuje informace o modulu: -jméno, dokumentační řetězec a seznam funkcí. -Kdybychom potřebovali kromě funkcí definovat i třídy nebo konstanty, -zde bychom pomocí [slotů][PyModuleDef_Slot] definovali funkci, která modul -inicializuje, t.j. má podobnou funkci jako `__init__` u třídy v Pythonu. - -[PyModuleDef_Slot]: https://docs.python.org/3/c-api/module.html#c.PyModuleDef_Slot - -Poslední část je funkce `PyInit`, jediná která není definována jako `static`, -takže jediná, která je exportována jako API knihovny, kterou vytváříme. -Až bude Python tento modul importovat, najde tuto funkci podle jména, spustí ji -a podle vrácené struktury typu `PyModuleDef` vytvoří pythonní objekt s modulem. - - -Překlad -------- - -Abychom mohli takovýto modul naimportovat, musíme ho nejdřív přeložit a sestavit -z něj sdílenou knihovnu – soubor .so (nebo .dll) – s názvem modulu: -buď jen `demo.so`, nebo i s identifikací architektury a verze Pythonu, -např. `demo.cpython-35m-x86_64-linux-gnu.so`. -(Výhoda delších názvů je v tom, že v jednom adresáři může být víc modulů pro -různé architektury a že se Python nebude snažit načíst nekompatibilní moduly.) - -Překlad je nutné provést se správnými přepínači a volbami, nejlépe takovými, -s jakými byl sestaven samotný Python. - -Pro zjednodušení tohoto procesu můžeme použít setuptools: do nám už známého -souboru `setup.py` přidáme argument `ext_modules` se seznamem rozšiřovacích modulů. -Podrobný popis třídy `Extension` je v [dokumentaci][Extension]; nám bude stačit -jen jméno a seznam zdrojových souborů: - -[Extension]: https://docs.python.org/3/distutils/apiref.html#distutils.core.Extension - -setup.py: - -```python -from setuptools import setup, Extension - -module1 = Extension( - 'demo', - sources=['demo.c'], -) - -setup( - name = 'demo', - version = '0.1', - description = 'Demo package', - ext_modules = [module1] -) -``` - -Příkazy `python setup.py sdist` a `python setup.py install` budou fungovat jako normálně, -jen je na instalaci potřeba překladač jazyka C. - -Aby uživatelé překladač mít nemuseli, můžeme nainstalovat knihovnu `wheel` (`python -m pip install wheel`) a pak příkazem `python setup.py bdist_wheel` vygenerovat tzv. *wheel* archiv, -např. `dist/demo-0.1-cp35-cp35m-linux_x86_64.whl`. Tento archiv jde nahrát na PyPI a následně -nainstalovat, ovšem jen na architektuře a verzi Pythonu, pro které byl vytvořen. - -Existuje způsob, jak vytvořit co nejvíce platformě nezávislý linuxový wheel. -Jedná se o platformu nazvanou `manulinux1`, což je ve zkratce velmi stará verze -Linuxu (CentOS 5), na které se wheely vytvoří, aby šly použít na různých -novějších i relativně starých distribucích. Pro tvorbu wheelů se používá -[Docker obraz manylinux](https://github.com/pypa/manylinux), -vývojáři samozřejmě nepoužívají pro vývoj CentOS 5 (tedy většina ne). - -> [note] -> Zajímavým nástrojem, který stojí za zmínku, je [cibuildwheel]. -> Zjednodušuje tvorbu wheelů pro Linux, macOS i Windows pomocí -> CI služeb [Travis CI] a [AppVeyor]. - -[cibuildwheel]: https://github.com/joerick/cibuildwheel#cibuildwheel -[Travis CI]: https://travis-ci.org/ -[AppVeyor]: https://www.appveyor.com/ - -Wheels jdou vytvářet i pro moduly tvořené jen pythonním kódem. -Nejsou pak vázané na verzi a architekturu. -Jejich výhoda oproti `sdist` archivům spočívá v tom, že se rychleji instalují. - -Alternativa k instalaci, alespoň pro lokální vývoj, je rozšíření jen přeložit a dát do -aktuálního adresáře (nebo jakéhokoli jiného adresáře, odkud se importují moduly). -K tomu slouží příkaz `python setup.py build_ext --inplace`. -Pozor na to, že po každé změně zdrojového kódu je potřeba rozšíření znovu přeložit. - -Příkaz `python setup.py develop` bude fungovat jako dřív (používá `build_ext --inplace`), -jen je opět potřeba příkaz po každé změně znovu spustit. - - -PyObject --------- - -Podívejme se teď na základní mechanismy interpretu CPython. - -Základní datová struktura, která reprezentuje jakýkoli objekt Pythonu, je PyObject -([dokumentace](https://docs.python.org/3/c-api/structures.html#c.PyObject), -[definice](https://github.com/python/cpython/blob/3.5/Include/object.h#L106)). -Skládá se ze dvou prvků: - -```c -typedef struct _object { - Py_ssize_t ob_refcnt; - struct _typeobject *ob_type; -} PyObject; -``` - -První je počet referencí (*reference count*), který se dá popsat jako počet míst, -ze kterých je možné k tomuto objektu přistoupit. -Když objekt uložíme do proměnné nebo do seznamu, zvýší se počet referencí o 1. -Když seznam nebo proměnná zanikne (nebo náš objekt přepíšeme jiným), -počet referencí se zase sníží. -Když počet referencí dosáhne nuly, znamená to, že se k objektu už nedá dostat a Python ho -uvolní z paměti. - -Druhý prvek struktury PyObject je ukazatel na typ. -Typ je pythonní objekt (`class`), který definuje chování třídy objektů: operátory, -atributy a metody, které ten objekt má. - -Struktura PyObject slouží jako hlavička, za kterou pak následují data interpretovaná podle -typu daného objektu. -Například pythonní [objekt typu float][float] vypadá následovně: - -```c -typedef struct { - PyObject ob_base; - double ob_fval; -} PyFloatObject; -``` - -...tedy struktura PyObject, za kterou je v paměti číselná hodnota. - -[Seznamy][list] obsahují za hlavičkou např. velikost a (ukazatel na) pole ukazatelů na jednotlivé -prvky. -Podobně [objekty typu int][int] (které mají v Pythonu neomezený rozsah) mají délku a pole -jednotlivých 30bitových „číslic“. -NumPy matice mají metadata (velikost, typ, popis rozložení v paměti) a ukazatel na pole hodnot. - -[float]: https://github.com/python/cpython/blob/3.5/Include/floatobject.h#L15 -[list]: https://github.com/python/cpython/blob/3.5/Include/listobject.h#L23 -[int]: https://github.com/python/cpython/blob/3.5/Include/longintrepr.h#L89 - -To základní, co potřebujeme vědět, je, že na úrovni C je každý pythonní objekt reprezentován -jako struktura počtu referencí, ukazatele na typ a dat specifických pro daný typ. - - -Reference counting ------------------- - -Tak jako v C je důležité správně alokovat a dealokovat paměť, při tvorbě rozšíření do CPythonu -je třeba správně pracovat s referencemi: ke každému [Py_INCREF] (přičtení 1 k počtu referencí) -je potřeba později zavolat [Py_DECREF] (odečtení 1 a případné uvolnění objektu). -Jakákoli práce s objektem se smí provádět jen mezi INCREF a příslušným DECREF. - -Platí konvence, že argumenty funkcí se předávají jako tzv. *borrowed reference*: o počitadlo -se stará volající a v průběhu volané funkce se objekt dá používat. -Pokud bychom ale argument potřebovali i po skončení volané funkce (např. si ho uložíme -do globální proměnné), je potřeba mu počitadlo zvýšit (a po skončení práce zase snížit). - -V našem modulu `demo` přebíráme jako parametr n-tici. -Zodpovědnost zavolat na tuto n-tici Py_DECREF má ale volající, ne my. -Zavoláním funkce `PyArg_ParseTuple` získáme `char*`, který ale můžeme používat jen v rámci naší -funkce: po jejím skončení může volající argumenty funkce uvolnit, a tím řetězec zrušit. - -Funkce, které vracejí pythonní objekty, předpokládají, že na vrácenou hodnotu provede DECREF volající. -V modulu `demo` voláme funkci [PyLong_FromLong], která vytvoří nové pythonní číslo. -Za vzniklou referenci naše funkce přebírá zodpovědnost, je tedy na nás, abychom se postarali -o zavolání Py_DECREF. -Vrácením výsledku tuto zodpovědnost ale předáváme na funkci, která volá tu naši. - -[Py_INCREF]: https://docs.python.org/3/c-api/refcounting.html#c.Py_INCREF -[Py_DECREF]: https://docs.python.org/3/c-api/refcounting.html#c.Py_DECREF -[PyLong_FromLong]: https://docs.python.org/3/c-api/long.html#c.PyLong_FromLong - - -Hodnoty a výjimky ------------------ - -Další konvence, kterou většina funkcí v C API dodržuje, je způsob vracení výjimek. - -Funkce, které vrací pythonní objekty, na úrovni C vrací `PyObject*`. -Nastane-li výjimka, objekt výjimky se zaznamená do globální (přesněji, *thread-local*) -proměnné a funkce vrátí NULL. - -V našem modulu `demo` voláme funkci `PyArg_ParseTuple`, která může vyvolat výjimku: typicky -`TypeError` kvůli nesprávnému počtu nebo typu argumentů. -V takovém případě tato funkce výjimku zaznamená a vrátí NULL. -Naší funkci `system` už stačí vrátit NULL, protože víme, že výjimka už je zaznamenaná. - -Další funkce, která může neuspět, je `PyLong_FromLong`. -Vzhledem k tomu, že její výsledek rovnou vracíme, není potřeba úspěch kontrolovat – vrátíme -buď správnou hodnotu nebo NULL se zaznamenanou výjimkou. - - -GIL ---- - -Poslední omezení, kterého si autor rozšíření musí být vědom, je *Global Interpreter Lock*. -Stručně řečeno, s objekty `PyObject*` může pracovat pouze jedno vlákno. -Toto vlákno drží globální zámek, který čas od času odemkne a znovu se pokusí zamknout, -aby mohly běžet i ostatní vlákna. - -Díky GIL je vícevláknové programování v Pythonu relativně bezpečné: nemůže např. nastat souběh -(*race condition*), kdy by se nastavilo počitadlo referencí na špatnou hodnotu. -Na druhou stranu tento zámek ale omezuje paralelismus, a tedy i rychlost programu. - -Globální zámek se dá odemknout v situacích, kdy nepracujeme s `PyObject*` a nevoláme pythonní kód. -Například čtení ze souboru nebo sítě ostatní vlákna neblokuje. -Stejně tak maticové operace v NumPy typicky nedrží GIL zatímco počítají na úrovni C nebo Fortranu. - - -Cython -====== - -Teď, když víme jak to všechno funguje, se můžeme podívat na způsob, jak rozšíření psát -jednoduše. -C API se totiž dá použít nejen z C, ale z jakéhokoli jazyka, který umí volat funkce se -stejnými konvencemi, např. C++ (s pomocí `extern C`). -Další způsob, jak použít C API ale nepsat C, je použít překladač z příjemnějšího jazyka do C. - -Jeden takový jazyk je Cython (neplést s CPython). - -Cython je jazyk podobný Pythonu, který ale lze přeložit na C a dále optimalizovat. - -Cython si nainstalujte pomocí příkazu: - -```console -$ python -m pip install cython -``` - - -Kompilace Pythonu ------------------ - -Když chceme převést modul z Pythonu do Cythonu, nejjednodušší začátek je přejmenovat soubor `.py` -na `.pyx`, aby bylo jasné, že jde o jiný jazyk, který nepůjde naimportovat přímo. - - -Jazyky Python a Cython nejsou 100% kompatibilní, ale zvláště u kódu, který pracuje hlavně s -čísly, se nekompatibilita neprojeví. -Vývojáři Cythonu považují každou odchylku od specifikace jazyka za chybu, kterou je nutno opravit. - -Jako příklad můžete použít tuto naivní implementaci celočíselného a maticového násobení. -Uložte si ji jako `matmul.py`: - -```python -import numpy - -def intmul(a, b): - result = a * b - return result - -def matmul(a, b): - n = a.shape[0] - m = a.shape[1] - if b.shape[0] != m: - raise ValueError('incompatible sizes') - p = b.shape[1] - result = numpy.zeros((n, p)) - for i in range(n): - for j in range(p): - for k in range(m): - x = a[i, k] - y = b[k, j] - result[i, j] += x * y - return result -``` - -Stáhněte si [testy](static/test_matmul.py) a zkontrolujte, že prochází. - -Potom soubor přejmenujte na `matmul.pyx`. - -Výsledek bychom mohli převést na C pomocí příkazu `cython -3 matmul.pyx`, čímž -vznikne `matmul.c`. Ten můžeme přeložit výše uvedeným způsobem. - -Jednodušší varianta je použít Cython v `setup.py`. -Pro naše účely bude `setup.py` s Cythonem a NumPy vypadat takto: - -```python -from setuptools import setup -from Cython.Build import cythonize -import numpy - -setup( - name='matmul', - ext_modules=cythonize('matmul.pyx', language_level=3), - include_dirs=[numpy.get_include()], - setup_requires=[ - 'Cython', - 'NumPy', - ], - install_requires=[ - 'NumPy', - ], -) -``` - -> [note] -> V případě problémech s nefungujícím `include_dirs` na systému macOS -> použijte komplikovanější variantu: -> ```python -> from distutils.extension import Extension -> ... -> ext_modules = cythonize([Extension('matmul', ['matmul.pyx'], -> include_dirs=[numpy.get_include()])], -> language_level=3) -> ``` - -Po zadání `python setup.py develop` nebo `python setup.py build_ext --inplace` atp. -se modul `matmul.pyx` zkompiluje s použitím nainstalovaného NumPy a bude připraven na použití. -(Zkontrolujte, že testy prochází i se zkompilovaným modulem.) - -Nevýhoda tohoto přístupu je, že k spuštění takového `setup.py` je již potřeba -mít nainstalovaný `cython` a `numpy`. -Instalace z archivu `sdist` se tedy nemusí povést – je potřeba uživatelům říct, -že dané moduly už musí mít nainstalované. -Tento problém aktuálně řeší PyPA (správci `pip` a `setuptools`). - -Instalace z archivů `wheel` by měla být bezproblémová. - - -Anotace -------- - -Kód, který takto vznikne, není o moc rychlejší než původní Python. -Je to tím, že sekvence příkazů ve funkci je sice převedená do C a přeložená do strojového kódu, -ale každá operace pracuje s generickými pythonními objekty, takže musí pro každé číslo -číslo z matice zkonstruovat pythonní objekt, vyhledat implementaci sčítání pro dvě celá čísla, -a výsledek převést zpět na `int64` a uložit do matice. - -Na situaci se můžeme podívat pomocí přepínače `--annotate`: - -```console -$ cython -3 --annotate matmul.pyx -``` - -To vygeneruje soubor `matmul.html`, kde jsou potencionálně pomalé operace vysvíceny žlutě. -Ke každému řádku se navíc dá kliknutím ukázat odpovídající kód v C (který bývá docela složitý, -protože řeší věci jako zpětnou kompatibilitu a ošetřování chyb, a navíc používá hodně pomocných -maker). - -Obecně nebývá problém mít „žluté“ ty řádky, které se ve funkci provádí pouze jednou. -Ale v cyklech, zvláště těch třikrát zanořených, se autor rozšíření typicky snaží žlutým řádkům -vyhnout. -Nejjednodušší způsob, jak toho docílit, je doplnění statických informací o typech. - - -Doplnění typů -------------- - -Začneme u funkce `intmul`, kde doplníme informaci o tom, že parametry `a` a `b` a proměnná -`result` jsou typu `int`. -Parametrům stačí doplnit typ podobně jako v C, ostatní lokální proměnné potřebují definici pomocí -příkazu `cdef`: - -```python -def intmul(int a, int b): - cdef int result - result = a * b - return result -``` - -Teď bude funkce nepatrně rychlejší, ale také méně obecná: nejde jí násobit řetězec číslem, -ale ani reálná čísla (`float`), a dokonce ani celá čísla, která se nevejdou do 64 bitů (příp. -jiné velikosti, dle systému). -Typ int v Cythonu je totiž int z C, ne ten neomezený z Pythonu. - -Další věc, kterou můžeme udělat, je změnit příkaz `def` na `cpdef` a doplnit typ návratové -hodnoty: - -```python -cpdef int intmul(int a, int b): - cdef int result - result = a * b - return result -``` - -Tím se zbavíme nákladného převodu výsledku na PyObject. -Bohužel ale toto zrychlení pocítíme, jen když takovou funkci zavoláme -z jiné funkce napsané v Cythonu. - -Tři typy funkcí ---------------- - -Funkce jdou deklarovat třemi způsoby: - - * `def func(...):` je funkce, která jde volat z Pythonu i z Cythonu, ale volání z Cythonu je pomalé (argumenty a výsledek se převádí na pythonní objekty a zpět), - * `cdef <type> func(...):` je funkce, která jde volat pouze z Cythonu, ale volání je rychlé (pracuje se s C typy), - * `cpdef <type> func(...):` je funkce, která se z Cythonu volá rychle, ale jde volat i z Pythonu (ve skutečnosti Cython vytvoří dva druhy této funkce). - -Třídy ------ - -Cython umožňuje vytvářet tzv. *built-in* třídy: stejný druh tříd jako je -např. `str` nebo `int`. -Práce s takovými třídami je rychlejší, ale mají pevně danou strukturu. -Ani jim ani jejich instancím nelze z Pythonu nastavovat nové atributy: - -```python ->>> "foo".bar = 3 -Traceback (most recent call last): - File "<stdin>", line 1, in <module> -AttributeError: 'str' object has no attribute 'bar' -``` - -Příklad definice *built-in* třídy: - -```python -cdef class Foo: - # Všechny členské proměnné musí být nadefinované tady - cdef int foo - ... - - def __cinit__(self, int f): - # Inicializace třídy. - # Cython zajistí, že se tato funkce zavolá pouze jednou (na rozdíl - # od __init__, kterou lze z pythonního kódu zavolat kdykoli) - self.foo = f - ... - - def __dealloc__(self): - # Deinicializace třídy - ... - - cpdef int method(self): - ... - return self.foo -``` - -Více o definici tříd najdete v [dokumentaci Cythonu](http://cython.readthedocs.io/en/latest/src/tutorial/cdef_classes.html). - -Používání NumPy ---------------- - -Pro funkci `matmul` můžeme nadefinovat číselné proměnné (`n`, `m`, `p`, `i`, `j`, `k`, `x`, `y`) -jako `int`, ale tím si moc nepomůžeme: většinu času program stráví vybíráním a ukládáním hodnot -z/do matic, a protože Cython nemá informace o tom, že jsou to NumPy matice, používá obecný -protokol pro pythonní kontejnery, takže se každá hodnota převede na pythonní objekt. - -Je tedy potřeba říct Cythonu, že používáme NumPy matice. -Naštěstí v NumPy existuje integrace s Cythonem, takže můžeme na úrovni C „naimportovat“ -rozšíření pro NumPy: - -```python -cimport numpy -``` - -... a potom použít typ „dvourozměrná matice celých čísel“, který se v Cythonu jmenuje -`numpy.ndarray[numpy.int64_t, ndim=2]`. -Naše funkce tedy bude začínat takto: - -```python -cpdef numpy.ndarray[numpy.int64_t, ndim=2] matmul( - numpy.ndarray[numpy.int64_t, ndim=2] a, - numpy.ndarray[numpy.int64_t, ndim=2] b): - cdef numpy.ndarray[numpy.int64_t, ndim=2] result - ... -``` - -Kdybychom si nebyli jistí typem matice, můžeme si ho nadefinovat pomocí `ctypedef`: - -```python -ctypedef numpy.int64_t DATATYPE -``` - -...a pak používat tento alias. -Na maticové typy bohužel typedef zatím nefunguje. - -Pro práci s maticí ASCII znaků lze použít typ `numpy.int8_t`, ale je třeba při zapisování přímo na konkrétní pozice zapisovat číselný typ `char`: - -```python -cdef numpy.ndarray[numpy.int8_t, ndim=2] directions = numpy.full((h, w), b'#', dtype=('a', 1)) -directions[maze >= 0] = b' ' # Python level, using b' ' -directions[1, 2] == ord('x') # C level, using char -``` - -> [note] -> Použití `matrix[a, b]` je v Cythonu rychlejší než `matrix[a][b]`, protože se -> uvnitř dějí jiné věci. Při použití `matrix[a, b]` u matice deklarované jako -> dvourozměrné pole nějakého typu Cython přistoupí přímo k obsahu na úrovni -> jazyka C. Při použití `matrix[a][b]` se ale dějí operace dvě, nejprve -> `matrix[a]` vrací jeden řádek matice a až poté `[b]` vrací jeden prvek z -> tohoto řádku. Obě operace probíhají na úrovni Pythonu a proto budou pomalejší -> a při použití `--annotate` bude řádek s takovou operací označen žlutě. - - -Direktivy ---------- - -Anotací typů matic se naše demo maticového násobení dostalo skoro na úroveň -C, ale ne úplně: řádky, které pracují s maticemi, jsou ve výstupu `--annotate` -stále trochu žluté. -Cython totiž při každém přístupu k matici kontroluje, jestli nečteme nebo -nezapisujeme mimo pole a případně vyvolá `IndexError`. - -Pokud víme – jako v našem případě – že je taková kontrola zbytečná, -můžeme Cythonu říct, aby ji nedělal. -Přístupy mimo pole pak způsobí nedefinované chování (většinou program spadne, -nebo hůř, bude pracovat se špatnými daty). -Kontrola se vypíná direktivou `boundscheck`, která se dá zadat dvěma hlavními -způsoby: dekorátorem: - - @cython.boundscheck(False) - cpdef funkce(): - ... - -... nebo příkazem `with`: - - with cython.boundscheck(False): - ... - -... případně i pro celý soubor, viz [dokumentace][set-directives]. - -Další zajímavá direktiva je `cython.wraparound(False)`, která podobným způsobem -vypíná pythonní způsob indexování zápornými čísly: místo indexování od konce -s ní dostaneme nedefinované chování. - -Seznam dalších direktiv najdete v [dokumentaci][directives]. - -Cython podporuje ještě blok `with cython.nogil:`, který je podobný direktivám, -ale dá se použít jen s `with`. -V rámci tohoto bloku je odemčený GIL (globální zámek). -Smí se použít, pouze pokud nepracujeme s pythonními objekty – například když -operujeme jen na obsahu už existujících maticí. -Opak je `with cython.gil:`, kterým zámek zase zamkneme – například když -potřebujeme vyhodit výjimku. - -[set-directives]: https://cython.readthedocs.io/src/userguide/source_files_and_compilation.html#how-to-set-directives -[directives]: https://cython.readthedocs.io/src/userguide/source_files_and_compilation.html#compiler-directives - - -Struktury, ukazatele a dynamická alokace ------------------------------------------ - -Přestože v Cythonu můžete používat pythonní *n*-tice, slovníky, seznamy a další -podobné nehomogenní typy, jejich použití je pomalé, protože vždy pracují -s pythonními objekty. - -Pokud máte kód, který potřebuje dočasné pole takových záznamů, -je pro časově kritické části kódu lepší k problému přistoupit spíše „céčkovsky“, -přes alokaci paměti a ukazatele. - -Následující příklad ukazuje, jak naplnit pole heterogenních záznamů: - -```python -# Import funkcí pro alokaci paměti – chovají se jako malloc() apod. -from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free - -# Definice struktury -cdef struct coords: - int row - int column - char data - -MAXSIZE = ... - -def path(...): - # Definice ukazatele, přetypování - cdef coords * path = <coords *>PyMem_Malloc(MAXSIZE*sizeof(coords)) - if path == NULL: - # nedostatek paměti - raise MemoryError() - - cdef int used = 0 - for ...: - ... - - # - path[used] = coords(row, column, data) - used += 1 - - # pole můžeme používat - ... - - # a musíme ho před vrácením předělat na list - lpath = [] - cdef int i - for i in range(used): - lpath.append(path[i]) - - # a uvolnit - PyMem_Free(path) - return lpath -``` - -Pro homogenní pole ale doporučujeme spíše NumPy matice. - -Následující příklad ukazuje, jak lze přiřazovat do struktur: - -```python -cdef struct coord: - float x - float y - float z - -cdef coord a = coord(0.0, 2.0, 1.5) - -cdef coord b = coord(x=0.0, y=2.0, z=1.5) - -cdef coord c - -c.x = 42.0 -c.y = 2.0 -c.z = 4.0 - -cdef coord d = {'x':2.0, - 'y':0.0, - 'z':-0.75} -``` - -Použití knihoven z C --------------------- - -Pro použití C knihoven z Pythonu je lepší použít [CFFI]. -Ale když už píšete kód v Cythonu -a potřebujete zavolat nějakou C funkci, můžete to udělat takto: - -```python -cdef extern from "stdlib.h": - int rand() - void srand(long int seedval) - -cdef extern from "time.h": - ctypedef long time_t - long int time(time_t *) - -srand(time(NULL)) -print(rand()) -``` - -Deklarace můžete vložit přímo do `.pyx` souboru, ale pokud je chcete používat -z různých míst, pojmenujte soubor `.pxd`, to vám umožní na něj použít `cimport`. - -Pro části standardní knihovny jsou takové deklarace již v Cythonu -předpřipravené, můžete tedy použít `cimport` rovnou: - -```python -from libc.stdlib cimport rand, srand -from libc.time cimport time - -srand(time(NULL)) -print(rand()) -``` - - -Zkratky: `pyximport` a `%%cython` ---------------------------------- - -Pro interaktivní práci v Jupyter Notebook má Cython vlastní „magii“. -Na začátku Notebooku můžeme zadat: - -```python -%load_ext cython -``` - -a potom můžeme na začátku kterékoli buňky zadat `%%cython`: - -```python -%%cython - -cpdef int mul(int a, int b): - return a * b -``` - -Kód v takové buňce pak Notebook zkompiluje Cythonem a funkce/proměnné v něm -nadefinované dá k dispozici. - -Můžeme použít i `%%cython --annotate`, což vypíše anotace přímo do Notebooku. - -Další zkratka je modul `pyximort`, který dává možnost importovat moduly `.pyx` -přímo: hledají se podobně jako `.py` nebo `.so` a před importem se zkompilují. -Zapíná se to následovně: - -```python -import pyximport -pyximport.install() - -import matmul -``` - - -Video ------ - -Před nedávnem měl [Miro] na Středisku unixových technologií nahrávanou ukázku přepsání -úlohy ruksaku z předmětu MI-PAA z Pythonu do Cythonu (včetně nepříjemného záseku a live -ukázky debugování problému). -Na [video] se můžete podívat, mohlo by vám prozradit spoustu tipů, které se vám mohou hodit -ke splnění úlohy. -K obsahu jen dodáme, že místo `malloc` a `free` je lepší použít `PyMem_Malloc` a -`PyMem_Free` z ukázky výše. - -[Miro]: https://github.com/hroncok/ -[video]: https://www.youtube.com/watch?v=Ksv4RA6yhkY diff --git a/lessons/intro/cython/info.yml b/lessons/intro/cython/info.yml deleted file mode 100644 index 1b13fd89..00000000 --- a/lessons/intro/cython/info.yml +++ /dev/null @@ -1,5 +0,0 @@ -course: MI-PYT -title: Cython -style: md -attribution: Pro kurz MI-PYT na ČVUT napsali Petr Viktorin, Miro Hrončok a další, 2016-2017. -license: cc-by-sa-40 diff --git a/lessons/intro/decorators/index.md b/lessons/intro/decorators/index.md deleted file mode 100644 index eee0f12b..00000000 --- a/lessons/intro/decorators/index.md +++ /dev/null @@ -1,469 +0,0 @@ -# Dekorátory - -V této lekci se nebudeme věnovat žádné externí knihovně. Místo toho se seznámíme -s jednou vlastností Pythonu, kterou knihovny často využívají, a která obvykle -vypadá trochu magicky. - -Touto vlastností jsou *dekorátory*. - -*Dekorátory* se hodí tehdy, když potřebujeme upravit chování nějaké funkce, ale -nechceme ji přímo upravovat. - -## Teorie do začátku - -Co je to vlastně dekorátor? Dekorátor je vlastně jenom funkce, která dostane -jeden argument a vrátí jednu hodnotu. Je ale trochu speciální v tom, že jak -argument, tak návratová hodnota jsou zase jiné funkce. - -> [note] -> Funkcím, které operují nad jinými funkcemi, říkáme *funkce vyššího řádu*. - -Použití dekorátorů v kódu vypadá zhruba takto: - -```python -@dekorator -def funkce(): - pass -``` - -Tento zápis se zavináčem je jenom *syntaktický cukr*. Usnadňuje nám zápis, ale -chová se přesně stejně jako následující kód, na kterém je lépe vidět, že -`dekorator` je funkce: - - -```python -def funkce(): - pass -funkce = dekorator(funkce) -``` - -Na řádku za zavináčem může být libovolný výraz, který po vyhodnocení vrátí -funkci, která má požadované rozhraní. - - -## Přiklad 0 – registrace funkcí - -Jak už při programování bývá zvykem, náš první dekorátor nás pozdraví. - -Začneme s jednoduchým programem, který definuje funkci pro pozdrav a zavolá ji. - -```python -def ahoj(): - print("Ahoj") - - -if __name__ == "__main__": - ahoj() -``` - -Do tohoto programu bychom rádi přidali další pozdravy, a zavolali je všechny. - -```python -def ahoj(): - print("Ahoj") - - -def nazdar(): - print("Nazdar") - - -if __name__ == "__main__": - ahoj() - nazdar() -``` - -Tento přístup ale povede k tomu, že by na konci byl dlouhý seznam pozdravů. -Můžeme si funkce rovnou uložit do seznamu a potom přes něj jenom iterovat. - -```python -def ahoj(): - print("Ahoj") - - -def nazdar(): - print("Nazdar") - - -if __name__ == "__main__": - funkce = [ahoj, nazdar] - for f in funkce: - f() -``` - -A jako poslední krok přidáme dekorátor, který nám bude funkce rovnou přidávat -do seznamu. - -```python -funkce = [] - - -def pridej_pozdrav(func): - funkce.append(func) - return func - - -@pridej_pozdrav -def ahoj(): - print("Ahoj") - - -@pridej_pozdrav -def nazdar(): - print("Nazdar") - - -if __name__ == "__main__": - for f in funkce: - f() -``` - -Zkuste přidat ještě jeden pozdrav. - -> [note] -> V tomto příkladu jde o docela zbytečné použití dekorátorů. Ukazuje ale -> praktický způsob, jak řešit registraci funkcí. Stejné řešení používá -> například knihovna `flask` pro definování webových služeb nebo `click` pro -> vytváření příkazů pro terminál. - - -## Příklad 1 – trasování volání funkcí - -Podívejme se třeba na tuto na pohled nevinnou funkci. Počítá, jak vypadá *n*-té -číslo ve Fibonacciho posloupnosti. Funguje docela pěkně, pokud jí nezadáme jako -argument příliš velké číslo. Na autorově počítači příliš velká čísla začínají -kolem 35. - -```python -def fib(x): - """Spočítá x-té číslo ve Fibonacciho posloupnosti.""" - if x <= 1: - return x - return fib(x - 1) + fib(x - 2) -``` - -Napíšeme si jednoduchý dekorátor, který nám bude vypisovat informace o tom, co -se ve funkci děje. - -```python -def co_se_deje(func): - print("Aplikuju dekorátor") - return func - - -@co_se_deje -def fib(x): - """Spočítá x-té číslo ve Fibonacciho posloupnosti.""" - if x <= 1: - return x - return fib(x - 1) + fib(x - 2) - -if __name__ == "__main__": - print(fib(4)) -``` - -Tento dekorátor funkci nijak nemění. Akorát nám oznámí, že byl aplikovaný. V -těle dekorátoru ale můžeme nadefinovat novou funkci a vrátit ji. - -Zkusme si to: - -```python -def co_se_deje(func): - def nahradni_funkce(x): - return "Spočítej si to sám!" - - return nahradni_funkce -``` - -Nebo můžeme vrátit funkci, která akorát zavolá tu původní. - -```python -def co_se_deje(func): - def nahradni_funkce(x): - return func(x) - - return nahradni_funkce -``` - -Pojďme vracenou funkci rozšířit tak, aby vypisovala informace o tom, co dělá. - -```python -def co_se_deje(func): - def nahradni_funkce(x): - print(f"Voláme {func.__name__}({x})") - return func(x) - - return nahradni_funkce -``` - -Úkol: upravte dekorátor tak, aby vypisoval i vypočítanou hodnotu. - -{% filter solution %} -```python -def co_se_deje(func): - def nahradni_funkce(x): - print(f"Voláme {func.__name__}({x})") - vysledek = func(x) - print(f"Výsledek {func.__name__}({x}) = {vysledek}") - return vysledek - - return nahradni_funkce -``` -{% endfilter %} - -> [note] -> Tento dekorátor není úplně praktický. Pokud toho vypíše trochu víc, tak už se -> v tom logu nikdo nevyzná. Myšlenka jako taková ovšem není úplně špatná. Kdyby -> třeba dekorátor počítal, kolikrát se funkce spustí, a jak dlouho obvykle -> trvá, mohl by nám pomoct najít místa pro optimalizaci. - - -### Nápověda pro funkce - -Zkuste si v interaktivní konzoli Pythonu spustit následující příklad: - -```pycon ->>> help(print) -Help on built-in function print in module builtins: - -print(...) - print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) - - Prints the values to a stream, or to sys.stdout by default. - Optional keyword arguments: - file: a file-like object (stream); defaults to the current sys.stdout. - sep: string inserted between values, default a space. - end: string appended after the last value, default a newline. - flush: whether to forcibly flush the stream. -``` - -Dostaneme krátkou nápovědu o tom, jak používat funkci `print`. - -Odkud se tato nápověda bere? Z dokumentačního komentáře. Takže bychom měli -dostat pěknou nápovědu třeba i pro naši známou funkci `fib`. - -```pycon ->>> from fib import fib ->>> help(fib) -Help on function nahradni_funkce in module fib: - -nahradni_funkce(x) - ->>> -``` - -Něco je špatně. Protože jsme původní implementaci funkce `fib` pomocí -dekorátoru nahradili naší pomocnou funkcí, komentář se cestou ztratil. Mohli -bychom přidat dokumentační komentář k náhradní funkci, ale přece nebudeme -stejný kód kopírovat dvakrát. - -Standardní knihovna má naštěstí možnost, jak to snadno opravit. V modulu -`functools` je definovaný dekorátor `wraps`, který umí zkopírovat dokumentační -komentář a jméno z jedné funkce do druhé. - -```python -import functools - - -def co_se_deje(func): - @functools.wraps(func) - def nahradni_funkce(x): - pass -``` - -```pycon ->>> from fib import fib ->>> help(fib) -Help on function fib in module fib: - -fib(x) - Spočítá x-té číslo ve Fibonacciho posloupnosti. - ->>> -``` - -### Předávání všech argumentů. - -Při psaní dekorátorů je dobré myslet na to, jak moc univerzální by měly být. -Například náš `co_se_deje` momentálně funguje pouze pro funkce, které mají -jeden argument. - -To je ale docela hloupé omezení. Stejně dobře bychom mohli chtít sledovat -volání jiné funkce, která má třeba argumentů víc. - -Pokud dekorátor nepotřebuje vědět nic o argumentech funkce, je docela praktické -jej nadefinovat tak, aby byly prostě všechny předal dál, ať už jich je kolik -chce. - -To můžeme udělat následovně: - -```python -def co_se_deje(func): - @functools.wraps(func) - def nahradni_funkce(*args, **kwargs): - print(f"Voláme {func.__name__}{args}") - vysledek = func(*args, **kwargs) - print(f"Výsledek {func.__name__}{args} = {vysledek}") - return vysledek - return nahradni_funkce -``` - -Do n-tice `args` posbíráme všechny poziční argumenty, do slovníku `kwargs` -všechny pojmenované argumenty. A při volání dekorované funkce je všechny zase -předáme dál. - -Ve výstupu teď používáme pouze poziční argumenty. Přidání těch pojmenovaných je -cvičení pro čtenáře. - - -## Příklad 2 – opakování HTTP požadavků - -Pokud náš program musí pracovat s nějakou externí službou nebo systémem, může -se stát, že komunikace mezi nimi nebude vždy bezproblémová. Pěkný příklad je -třeba stahování webové stránky se špatným připojením. S tím z Pythonu nic -udělat nemůžeme. - -Můžeme ale zkusit požadavek zopakovat, pokud poznáme, že je to typ chyby, kde -opakování může pomoct. - -Začneme s jednoduchým programem, který udělá HTTP požadavek. - -> [note] -> Následující příklady používají knihovnu *requests*. Nainstalujte si ji, pokud ji -> ve virtuálním prostředí ještě nemáte. - -```python -import requests - -def stahni(): - """Stáhne stránku a něco s ní udělá.""" - print("Stahuju stránku") - odpoved = requests.get("https://httpbin.org/status/200,400,500") - print(f"Dostali jsme {odpoved.status_code}") - odpoved.raise_for_status() - return "OK" - - -if __name__ == "__main__": - stahni() -``` - -Použitá stránka náhodně odpoví jedním z vyjmenovaných kódu, takže ve dvou -třetinách případů bychom měli dostat chybu. Pokud požadavek zkusíme zopakovat, -máme dobrou šanci, že to projde. - -Začneme s jednoduchým dekorátorem, který jenom zavolá funkci. - -```python -def opakuj_pri_neuspechu(func): - """Pokud volání funkce vyhodí výjimku, budeme ji ignorovat a zkusíme funkci - zavolat znovu. - """ - @functools.wraps(func) - def nahradni_funkce(*args, **kwargs): - return func(*args, **kwargs) - - return nahradni_funkce - - -@opakuj_pri_neuspechu -def stahni(): - """Stáhne stránku a něco s ní udělá.""" - print("Stahuju stránku") - odpoved = requests.get("https://httpbin.org/status/200,400,500") - print(f"Dostali jsme {odpoved.status_code}") - odpoved.raise_for_status() - return "OK" -``` - -Co by měla dělat naše náhradní funkce? Donekonečna bude zkoušet zavolat -dekorovanou funkci. Pokud se to podaří, vrátí její výsledek. Pokud dostaneme -výjimku `requests.exceptions.HTTPError`, chvilku počkáme, a půjdeme na další -pokus. - -```python -import functools -import time - -import requests - - -def opakuj_pri_neuspechu(func): - """Pokud volání funkce vyhodí výjimku, budeme ji ignorovat a zkusíme funkci - zavolat znovu. - """ - - @functools.wraps(func) - def nahradni_funkce(*args, **kwargs): - while True: - try: - return func(*args, **kwargs) - except requests.exceptions.HTTPError: - print("Chyba, zkusíme to znovu") - time.sleep(1) - - return nahradni_funkce - - -@opakuj_pri_neuspechu -def stahni(): - """Stáhne stránku a něco s ní udělá.""" - print("Stahuju stránku") - odpoved = requests.get("https://httpbin.org/status/200,400,500") - print(f"Dostali jsme {odpoved.status_code}") - odpoved.raise_for_status() - return "OK" -``` - -Teď by program měl vypisovat, že se snaží stránku stáhnout několikrát, a -opakovat to tak dlouho, dokud se to nepodaří. - -Co když ale potřebujeme opakování pokusů na více místech, ale chceme reagovat -na jiné výjimky? - -Mohli bychom si nadefinovat nový dekorátor pro každý typ výjimky, kterou -chceme chytat. To zní jako hodně práce a duplicitního kódu. - -Místo toho můžeme dekorátor upravit tak, aby přijímal argumenty, a pak mu -s jejich pomocí řekneme, kterou výjimku ošetřovat. - -Výraz za `@` musí při vyhodnocení vždy vracet funkci, která se chová jako -dekorátor. Takže musíme přidat jednu vrstvu do našich vnořených funkcí. - -Funkce `opakuj_pri_neuspechu` je vlastně továrna na dekorátory. Vždy, když ji -zavoláme, vrátí nám funkci, která se chová podle našich potřeb a funguje jako -dekorátor. - -```python -import functools -import time - -import requests - - -def opakuj_pri_neuspechu(vyjimka): - - def dekorator(func): - - @functools.wraps(func) - def nahradni_funkce(*args, **kwargs): - while True: - try: - return func(*args, **kwargs) - except vyjimka: - print("Chyba, zkusíme to znovu") - time.sleep(1) - - return nahradni_funkce - - return dekorator - - -@opakuj_pri_neuspechu(requests.exceptions.HTTPError) -def stahni(): - """Stáhne stránku a něco s ní udělá.""" - print("Stahuju stránku") - odpoved = requests.get("https://httpbin.org/status/200,400,500") - print(f"Dostali jsme {odpoved.status_code}") - odpoved.raise_for_status() - return "OK" -``` diff --git a/lessons/intro/decorators/info.yml b/lessons/intro/decorators/info.yml deleted file mode 100644 index bc9c74bd..00000000 --- a/lessons/intro/decorators/info.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Dekorátory -style: md -attribution: Lubomír Sedlář, Lumír Balhar 2019-2020. -license: cc-by-sa-40 diff --git a/lessons/intro/deployment/index.md b/lessons/intro/deployment/index.md deleted file mode 100644 index 68ba3be3..00000000 --- a/lessons/intro/deployment/index.md +++ /dev/null @@ -1,37 +0,0 @@ -Deployment webových aplikací -============================ - -Aplikace napsaná v Pythonu běží na našem počítači, ale jak ji dostat do Internetu? -Existují různé možnosti, jednou z nich je nasadit ji do cloudu. - -> [note] -> Nemáte ještě webovou aplikaci? Můžete vyzkoušet framework -> [Flask](../../intro/flask/). - -### WSGI - -Nasazování webových aplikací v Pythnu se opírá o WSGI, -což je standardní pythonní rozhraní pro komunikaci -mezi webovou aplikací a webovým serverem definované v [PEPu 333][PEP333]. - -Naprostá většina webových frameworků v Pythonu toto rozhraní implementuje přímo, -případně k tomuto účelu obsahuje wrapper. - -Je tedy jedno, jestli používáte Flask, Pyramid, Django, Bottle nebo Falcon, -vždy vaší aplikaci představuje `application` objekt, který se navenek chová -stejně. Webové frameworky implementují aplikační část WSGI. - -Stejně tak existují webové servery, které implementují serverovou část WSGI, -například [Gunicorn] nebo `mod_wsgi` pro `httpd` (Apache). Tyto servery umí -pracovat s `application` objektem a nezajímá je, v jakém frameworku je aplikace -napsaná. - -[PEP333]: https://www.python.org/dev/peps/pep-0333/ -[Gunicorn]: http://gunicorn.org/ - -Většině cloudových providerům stačí nějakým způsobem `application` objekt předat -a o zbytek se postarají za vás. Jedním z takových providerů je i -[PythonAnywhere](https://www.pythonanywhere.com/). - -Deployment webových aplikací na PythonAnywhere je popsaný v lekci -[PythonAnywhere]({{ subpage_url('pythonanywhere') }}). diff --git a/lessons/intro/deployment/info.yml b/lessons/intro/deployment/info.yml deleted file mode 100644 index d9189706..00000000 --- a/lessons/intro/deployment/info.yml +++ /dev/null @@ -1,7 +0,0 @@ -title: Deployment webových aplikací -style: md -attribution: Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017. -license: cc-by-sa-40 -subpages: - pythonanywhere: - subtitle: PythonAnywhere diff --git a/lessons/intro/deployment/pythonanywhere.md b/lessons/intro/deployment/pythonanywhere.md deleted file mode 100644 index cc572df7..00000000 --- a/lessons/intro/deployment/pythonanywhere.md +++ /dev/null @@ -1,117 +0,0 @@ -Deployment webových aplikací na PythonAnywhere -============================================== - -[PythonAnywhere] je pro limitované použití zdarma. - -K posílání kódu na produkční prostředí budeme používat Git. -Nejprve proto uložte celý projekt do Gitu a nahrajte na GitHub. - -Potom se zaregistrujte na -[www.pythonanywhere.com](https://www.pythonanywhere.com/) a vyberte -*Beginner Account*. -Po přihlášení se ukáže záložka *Consoles*, kde vytvořte "Bash" konzoli. -V té vytvořte a aktivujte virtuální prostředí a nainstalujte Flask (plus -případně další závislosti nebo jiný webový framework). - -PythonAnywhere používá specificky nastavený Linux, -tak je ve webové konzoli potřeba použít jiný příkaz -na vytvoření virtuální prostředí, než jste z toho kurzu zvyklí. -Napište příkazy takto (bez úvodního `$`): - -```console -$ virtualenv --python=python3.7 __venv__ -$ . __venv__/bin/activate -$ python -m pip install flask -``` - -> [note] -> Pokud máte na PythonAnywhere starší účet, možná tam Python 3.7 nenajdete. -> Můžete použít Python 3.6, nemělo by to vadit, protože tento návod je -> koncipován tak, aby s touto verzí také fungoval. -> Případně můžete [zažádat o aktualizaci systémové -> image](https://www.pythonanywhere.com/forums/topic/12878/#id_post_52160). - - -Následně naklonujte na PythonAnywhere váš kód. -S veřejným repozitářem je to jednodušší – stačí ho naklonovat „anonymně” -(`git clone https://github.com/<github-username>/<github-repo>`). -Pokud ale používáme privátní repozitář, bude potřeba si vygenerovat SSH klíč: - -```console -$ ssh-keygen # (zeptá se na hesla ke klíči) -$ cat ~/.ssh/id_rsa.pub -``` - -Obsah souboru `~/.ssh/id_rsa.pub` je pak potřeba přidat na GitHub v osobním -nastavení v sekci "SSH and GPG Keys". -Pak můžete klonovat přes SSH: - -```console -$ git clone git@github.com:<github-username>/<github-repo>.git -``` - -Zbývá nastavit, aby PythonAnywhere tento kód spustil jako webovou aplikaci. - -Přejděte na stránkách PythonAnywhere do *Dashboard* do záložky *Web*, -a vytvořte novou aplikaci. -V nastavení zvolte *Manual Configuration* a *Python 3.7*. -(Volby jiné než *Manual Configuration* automaticky vytvoří kostru aplikace. -Vy ale už aplikaci máte hotovou, takže je nepotřebujete.) - -V konfiguraci vzniklé webové aplikace je potřeba nastavit *Virtualenv* -na cestu k virtuálnímu prostředí (<code>/home/<var><uživatelské-jméno></var>/__venv__</code>), -a obsah *WSGI Configuration File* přepsat. -To jde buď kliknutím na odkaz v konfiguraci (otevře se webový editor) -nebo zpět v bashové konzoli pomocí editoru jako `vi` nebo `nano`. - -Nový obsah souboru by měl být: - -```python -import sys -path = '/home/<uživatelské-jméno>/<jméno-adresáře>' -if path not in sys.path: - sys.path.append(path) - -from <jméno-souboru> import app as application -``` - -(Za <code><var><uživatelské-jméno></var></code>, -<code><var><jméno-adresáře></var></code> a -<code><var><jméno-souboru></var></code> -je samozřejmě potřeba doplnit -vaše údaje. Jméno souboru je zde bez přípony `.py`.) - -Nakonec restartujte aplikaci velkým zeleným tlačítkem na záložce *Web* -a na adrese <code><var><uživatelské-jméno></var>.pythonanywhere.com</code> -si ji můžete prohlédnout. - -[PythonAnywhere]: https://www.pythonanywhere.com/ - -### Deployment soukromých údajů - -Protože vaše hesla, tajné klíče apod. nejsou v repozitáři, je nutné je předat -aplikaci zvlášť. -Konfigurační i jiné soubory jde nahrát v záložce *Files* nebo opět vytvořit -a editovat ve webové konzoli. - -Pokud vaše aplikace vyžaduje nastavení nějakých proměnných prostředí -(například s cestou ke konfiguračnímu souboru nebo přímo s nějakou konfigurací), -můžete tak učinit přímo z *WSGI Configuration File*. -Buďto „nízkoúrovňově“ (`os.environ`) nebo více sofistikovaně například pomocí modulu `dotenv`, -což ostatně [doporučují i v dokumentaci](https://help.pythonanywhere.com/pages/environment-variables-for-web-apps/). - -> [note] -> Doporučujeme pro tyto potřeby stejně raději nepoužívat API klíče -> k vlastním účtům, raději si vyrobte nějaké účty pouze pro tento účel. -> GitHub povoluje všem vytvořit si jeden účet pro automatické operace, ale -> takový účet musí mít napsané v popisu, že je robot. - - -### Aktualizace - -Když nahrajeme nový kód na GitHub, je vždy potřeba provést na PythonAnywhere -v konzoli `git pull` a pak v záložce *Web* aplikaci restartovat. - -Placená varianta PythonAnywhere má API a tento proces jde zautomatizovat. - -Ve verzi zadarmo to není tak pohodlné. diff --git a/lessons/intro/distribution/index.md b/lessons/intro/distribution/index.md deleted file mode 100644 index cd6a630c..00000000 --- a/lessons/intro/distribution/index.md +++ /dev/null @@ -1,589 +0,0 @@ -Moduly -====== - -Zatím jsme tvořili programy v Pythonu tak nějak na divoko, tedy v jednom nebo -více souborech bez nějakého zvláštního řádu. V této lekci se podíváme na -to, jak tvořit redistribuovatelné moduly a balíčky, které jdou nahrát na PyPI -(veřejný seznam balíčků pro Python) a instalovat pomocí nástroje pip. - -Za příklad si vezmeme kód Ondřeje Caletky, který umožňuje určit české svátky -v zadaném roce. Jako příklad je ideální, protože obsahuje jak funkce, které -můžeme volat z Pythonu, tak lze volat z příkazové řádky. - - * [oskar456/isholiday.py](https://gist.github.com/oskar456/e91ef3ff77476b0dbc4ac19875d0555e) - - -Volání z příkazové řádky, pomocí příkazu `python isholiday.py` nebo -`python -m isholiday`, zajišťuje blok `if __name__ == '__main__':`. -Toto je rychlý způsob, jak napsat modul, který jde jak importovat, tak spustit. -Když nějaký modul importujeme, má v proměnné `__name__` k dispozici své jméno. -„Hlavní” modul ale není importován a jeho jméno není vždy k dispozici -(např. v `cat isholiday.py | python`). -Python proto `__name__` „hlavního” modulu nastavuje na `'__main__'`, -čehož se často využívá. - -Později se podíváme na elegantnější způsob jak to zařídit; teď se vraťme -zpět k balíčkování. - -Slovníček pojmů ---------------- - -Než se pustíme do samotného výkladu, zavedeme některé pojmy tak, -aby mezi nimi nedošlo v textu záměně. -Anglické pojmy v závorce jsou převzaty z oficiálního [glosáře](https://packaging.python.org/glossary). - -* **(importovatelný) modul** (_Module_ ∪ _Import Package_) je cokoliv, - co se dá importovat z Pythonu, v tomto textu tedy především Python soubor nebo adresář s vícero Python soubory; -* **balíček** (_Distribution Package_) je instalovatelný archiv obsahující - _importovatelné moduly_ pro Python a další potřebné soubory, může být i rozbalený; -* **zdrojový balíček** (_Source Distribution_, `sdist`) je varianta zabaleného _balíčku_ ve zdrojové formě; -* **binární balíček** (_Binary Distribution_, `bdist`) je varianta zabaleného _balíčku_ v nezdrojové (např. zkompilované) formě, nejčastější podoba toho balíčku se jmenuje `wheel`; -* **projekt** (_Project_) je knihovna, framework, skript, plugin, aplikace apod. (či jejich kombinace), které balíme do _balíčků_; -* **build backend** je program, který vytváří z projektu balíček. Existuje mnoho takových programů, které mají různé vlastnosti a hodí se k různým účelům. - Jejich přehled najdete třeba na oficiálním [tutoriálu, jak tvořit Python balíčky](https://packaging.python.org/en/latest/tutorials/packaging-projects/). - - -pyproject.toml --------------- - -Základním stavebním kamenem Python balíčku je soubor `pyproject.toml`, který -obsahuje všechna potřebná metadata pro vytvoření zdrojového i binárního balíčku. -Soubor používá jazyk TOML, což je datový formát, který se dobře píše člověkem a čte počítačem. -Plnou specifikaci TOMLu můžete najít v oficiální [dokumentaci tohoto jazyka](https://toml.io/en/). - -Pojďme vytvořit minimální variantu souboru `pyproject.toml`. -Použijeme balíček `setuptools` jako náš build backend. - - -```toml -[build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" - -[project] -name = "isholiday" -version = "0.1.0" -authors = [ - { name="Ondřej Caletka", email="ondrej@caletka.cz" }, -] -description = "Finds Czech holiday for given year" -license = {text = "Public Domain"} - -[project.urls] -"Homepage" = "https://gist.github.com/oskar456/e91ef3ff77476b0dbc4ac19875d0555e" -``` - -Všimněte si, že jsme balíček pojmenovali stejně jako soubor se zdrojovým kódem -(tedy stejně jako modul). -Je to dobrá konvence, ale není to technicky nutné. - -Balíček můžeme zkusit nainstalovat do virtuálního prostředí: - -```console -$ python3.11 -m venv __venv__ # (nebo jinak -- podle vašeho OS) -$ . __venv__/bin/activate # (nebo jinak -- podle vašeho OS) -(__venv__)$ python -m pip install . -... -(__venv__)$ python ->>> import isholiday ->>> -(__venv__)$ python -m pip freeze -isholiday @ file:///tmp/isholiday # cesta k modulu bude u vás vypadat jinak -``` - -Alternativně můžete použít příkaz `pip install --editable`, -který balíček nainstaluje tak, že změny v souborech se projeví rovnou -(není třeba po každé změněně instalovat znovu). - -```console -(__venv__)$ python -m pip install --editable . -... -(__venv__)$ pip freeze -# Editable install with no version control (isholiday==0.1.0) --e /tmp/isholiday -``` - -Vytvoření zdrojového a binárního balíčku ----------------------------------------- - -Pomocí nástroje `pip` můžeme sice nainstalovat náš balíček do vlastního virtuálního prostředí, -ale neřešíme tím základní otázku: distribuci pro širokou veřejnost. - -Použijeme nástroj `build`, který přečte _recept na balíček_, obsažen v souboru `pyproject.toml`, -a vytvoří zdrojovou a binární distribuci. - -```console -(__venv__)$ python -m pip install build -... -(__venv__)$ python -m build -* Creating venv isolated environment... -* Installing packages in isolated environment... (setuptools>=61.0) -* Getting build dependencies for sdist... -... -Successfully built isholiday-0.1.0.tar.gz and isholiday-0.1.0-py3-none-any.whl -``` - -Všimněte si nového adresáře v projektu: `dist`, v němž jsou dva archivy: -- `isholiday-0.1.0.tar.gz` - zdrojový balíček -- `isholiday-0.1.0-py3-none-any.whl` - binární balíček (tzv. _wheel_). - -Můžete je rozbalit pomocí systémových nástrojů a podívat se dovnitř. - -> [note] -> `whl` je ve skutečnosti stejný formát jako `zip`, -> takže pokud ho vaše systémové nástroje neumí otevřít, -> stačí před otevřením změnit příponu souboru na `.zip`. - -Můžete také vytvořit pouze zdrojový nebo pouze binární balíček pomocí přepínačů `--sdist`, resp. `--wheel`. - - -Extra soubory do zdrojového balíčku ------------------------------------ - -Náš projekt nemá `README` – soubor, do kterého se tradičně píšou základní informace o projektu. -Můžeme jej vytvořit a uložit jako `README` přímo v kořenovém adresáři projektu, -tedy tam, kde byste jej nejspíš čekali. - -``` -Czech public holiday checker... -``` - -Pojďme vytvořit i další speciální soubor, `LICENSE`, který bude -obsahovat text licence, v tomto případě Public Domain. -Obsah najdete třeba na [CC0]. - -[CC0]: https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt - -Poté vytvoříme balíčky znovu: - -```console -(__venv__)$ python -m build -``` - -Zkontrolujte obsah zdrojového archivu (`isholiday-0.1.0.tar.gz`), který najdete v adresáři `dist`. -Měly by tam být oba soubory. - -Hotový balíček pak můžete nainstalovat pomocí nástroje `pip`. -Doporučuji to dělat v jiném virtuálním prostředí – v aktuálním už ho máte -nainstalovaný. - -```console -# v jiné konzoli, v jiném adresáři -$ python3 -m venv __venv2__ -$ . __venv2__/bin/activate -(__venv2__)$ python -m pip install cesta/k/projektu/dist/isholiday-0.1.0.tar.gz -Processing cesta/k/projektu/dist/isholiday-0.1.0.tar.gz -Installing collected packages: isholiday - Running setup.py install for isholiday ... done -Successfully installed isholiday-0.1.0 -``` - -Více metadat v pyproject.toml ------------------------------ - -Na chvíli se vrátíme k `pyproject.toml` a přidáme co nejvíc dalších -položek. -Jejich vysvětlení najdete [v dokumentaci](https://packaging.python.org/en/latest/specifications/declaring-project-metadata/#declaring-project-metadata). - -```toml -[build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" - -[project] -name = "isholiday" -version = "0.1.0" -authors = [ - { name="Ondřej Caletka", email="ondrej@caletka.cz" }, -] -description = "Finds Czech holiday for given year" -license = {file = "LICENSE"} -readme = {"README"} -requires-python = ">=3.6" -keywords = ['holiday', 'dates', 'Czech'] -classifiers = [ - 'Intended Audience :: Developers', - 'License :: Public Domain', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Topic :: Software Development :: Libraries', -] - -[project.urls] -"Homepage" = "https://gist.github.com/oskar456/e91ef3ff77476b0dbc4ac19875d0555e" - -``` - -Všimněte si argumentu `classifiers`. Jsou to -v podstatě takové tagy nebo strukturované informace o balíčku. -Zásadně si je nevymýšlíme sami, ale hledáme je v -[seznamu](https://pypi.org/pypi?%3Aaction=list_classifiers). -Tyto informace budou později vidět na [PyPI](https://pypi.org) a -půjde podle nich hledat. - - -Více souborů s Python kódem ---------------------------- - -Doteď jsme vytvářeli balíček jen s modulem ve formě jednoho zdrojového souboru `isholiday.py`. -Co ale dělat, pokud je náš projekt větší a obsahuje souborů více? - -Vytvoříme modul ve formě složky. V našem případě soubor -`isholiday.py` zatím přesuneme do `isholiday/__init__.py`: - -```console -(__venv__)$ tree -. -├── LICENSE -├── pyproject.toml -└── isholiday - └── __init__.py -``` - -Soubor `__init__.py` jednak značí, že adresář `isholiday` je importovatelný modul, -a také obsahuje kód, který se spustí při importu modulu `isholiday`. - -`setuptools` bude této změně automaticky rozumět, což si můžete vyzkoušet vytvořením a prozkoumáním balíčku. - -Momentálně máme všechen kód přímo v `__init__.py`, což sice funguje, -ale ideální to není. Dobré je mít kód v samostatných souborech a v `__init__.py` -pouze importovat veřejné rozhraní, tedy to, co budou z vašeho modulu importovat -jeho uživatelé. - -V souboru `__init__.py` by tak prakticky žádný kód kromě importů být neměl. -Přesuňte tedy obsah `__init__.py` do `holidays.py` a -do `__init__.py` místo toho napište: - -```python -from .holidays import getholidays, isholiday - -__all__ = ['getholidays', 'isholiday'] -``` - -Tečka v příkazu `import` není chyba: je to zkratka pro aktuální modul. -Můžeme psát i `from isholiday.holidays import ...`, -což ale trochu ztěžuje případné přejmenování modulu. - -Ono `__all__` pak explicitně definuje rozhraní modulu. Například s původním -modulem šlo provést `from isholiday import datetime`, ale asi by nikdo -nečekal, že tahle možnost bude nutně zachována i v příštích verzích knihovny. -Seznamem `__all__` dáte najevo, že tyhle funkce nejsou jen „náhodné importy“, -a zároveň tím zamezíte různým varováním o -importovaném ale nevyužitém modulu, které může hlásit vaše IDE nebo linter. - -> [note] -> Python samotný pak `__all__` používá jako seznam proměnných importovaných -> přes `from isholiday import *`. Tento způsob importu nevidíme rádi, -> protože znepřehledňuje kód, to ale neznamená, že to musíme uživatelům -> naší knihovny znepříjemňovat (např. pro interaktivní režim). - - -Spouštění modulu ----------------- - -Pokusíme-li se teď program spustit pomocí `python -m isholiday`, -narazíme na problém: na rozdíl od souboru se složka s kódem takto spustit nedá: - -```console -$ python -m isholiday -python: No module named isholiday.__main__; 'isholiday' is a package and cannot be directly executed -``` - -Namísto spuštění souboru (typicky s blokem `if __name__ == '__main__':`) totiž -Python v tomto případě hledá *soubor* pojmenovaný `__main__.py` a spustí ten. - -Soubor `__main__.py` není určený k tomu, aby se z něho importovalo, proto -by měl obsahovat co nejméně kódu – ideálně jen volání funkce, která je -definovaná jinde. Vytvořte proto `__main__.py` s následujícím obsahem: - -```python -from .holidays import main - -main() -``` - -a v `holidays.py` zaměňte `if __name__ == '__main__':` za `def main():`. - -Modul teď bude možné (opět) spustit pomocí `python -m isholiday`. -Bude to fungovat i tehdy, když vytvoříte balíček (`python -m build`) -a nainstalujete ho v jiném virtuálním prostředí. - - -Programy pro příkazovou řádku ------------------------------ - -Pokud chcete, aby váš modul umožňoval spouštění přímo z příkazové řádky, -bez `python -m`, měli byste použít [entry-points]. -K tomu je potřeba přidat do `pyproject.toml` příslušný argument: - -[entry-points]: https://packaging.python.org/en/latest/specifications/declaring-project-metadata/#entry-points - -```toml -[project.scripts] -isholiday_demo = "isholiday.holidays:main" -``` - -`isholiday_demo` je jméno *entrypointu*, tedy příkazu pro příkazovou řádku. -`isholiday.holidays:main` je pak cesta k funkci ve tvaru `modul:funkce`; -funkce může být v modulu definovaná nebo importovaná. - -Skript bude možné použít, je-li aktivní prostředí, kde je nainstalován, jen -zadáním jména *entrypointu*: - -```console -(__venv__)$ python -m build -``` - -```console -# v jiné konzoli, v jiném virtuálním prostředí -(__venv2__)$ python -m pip install --upgrade cesta/k/projektu/dist/isholiday-0.1.0.tar.gz -(__venv2__)$ isholiday_demo -... -Mon Mar 28 00:00:00 2016 True -Tue Mar 28 00:00:00 2017 False -Fri Apr 14 00:00:00 2017 True -``` - - -Specifikace závislostí ----------------------- - -Balíčky na PyPI mohou záviset na dalších balíčkách. V případě `isholiday` to -potřeba není, ale v úlohách z minulých cvičení ano. - -Existuje několik úrovní závislostí, ve většině případů si -vystačíte s klíčem `dependencies`. -Balíček, který závisí na balíčkách `Flask` (jakékoli verze) a -`click` (verze 6 a vyšší) by v `pyproject.toml` měl mít: - -```toml -dependencies = [ - "Flask", - "click>=6", -] -``` - -### Soubor requirements.txt - -Kromě závislostí v `pyproject.toml` se u pythonních projektů často setkáme se souborem -`requirements.txt`, který obsahuje přesné verze všech závislostí, včetně -tranzitivních – t.j. závisí-li náš balíček na `Flask` a `Flask` na `Jinja2`, -najdeme v `requirements.txt` mimo jiné například řádky: - -``` -Flask==0.11.1 -Jinja2==2.8 -``` - -Tento soubor se používá, když je potřeba přesně replikovat prostředí, kde -program běží, například mezi testovacím strojem a produkčním nasazením -webové aplikace. -Tento soubor se dá vygenerovat z aktuálního prostředí zadáním -`python -m pip freeze > requirements.txt` a balíčky v něm se dají nainstalovat -pomocí `python -m pip install -r requirements.txt`. -My ho používat nebudeme, vystačíme si s volnější specifikací závislostí -v `pyproject.toml`. - - -Nahrání na PyPI ---------------- - -Balíček jde zaregistrovat a nahrát na PyPI. Použijeme pro to program `twine` -(instalovatelný přes pip). - -Budete si potřebovat zařídit -[účet na testovací PyPI](https://test.pypi.org/account/register/). -Od roku 2023 proces registrace zahrnuje nastavení dvoufaktorové autentikace -pro práci s PyPI. -Budete pro to potřebovat TOTP aplikaci - pokud jste ji zatím nepoužívali, -podívejte se na [doporučení od administrátorů PyPI](https://pypi.org/help/#totp). -Poté, co si nastavíte dvoufaktorový přístup na účet, budete moci vytvořit API -token pro nahrávání balíčků. -Vytvořte nový token a vložte jeho hodnotu do konfiguračního souboru `~/.pypirc`: - -```ini -[distutils] -index-servers= - testpypi - -[testpypi] -username = __token__ -password = <TestPyPI token> -``` - -Používáte-li Windows, je potřeba nastavit proměnnou prostředí `HOME` na adresář -se souborem `.pypirc`, např: - -```dosvenv -> set HOME=C:\cesta\k\nastaveni -``` - -Registrace projektu a nahrání na testovací PyPI se provádí pomocí příkazu -`upload`: ten projekt zaregistruje (pokud to jde) a nahraje samotný balíček: - -```console -(__venv__)$ twine upload -r testpypi dist/isholiday-0.1.0.tar.gz -Uploading distributions to https://test.pypi.org/legacy/ -Uploading isholiday-0.1.0.tar.gz -[================================] 8379/8379 - 00:00:02 -``` - -První nahrání se zdaří, jen pokud jméno projektu již není zabrané. -Další nahrávání je povoleno jen vám, případně uživatelům, -kterým to povlíte přes webové rozhraní. -Po úspěšném nahrání lze nahrávat další verze balíčku, ale musí být novější -než ta, co už na PyPI je. Nejde tedy jednou nahraný balíček přepsat. - -Svůj balíček najdete na `https://test.pypi.org/project/<název_balíčku>/`. - -> [note] -> Až budete chtít nahrávat opravdové balíčky na PyPI, vytvořte si -> [účet na PyPI](https://pypi.org/account/register/). -> Všechny kroky budou třeba provést znovu, včetně nastavení -> dvoufaktorové autentikace a vygenerování nového tokenu. -> Pak aktualizujte konfigurační soubor `~/.pypirc`: -> -> ```ini -> [distutils] -> index-servers= -> pypi -> testpypi -> -> [pypi] -> username = __token__ -> password = <PyPI token> -> -> [testpypi] -> username = __token__ -> password = <TestPyPI token> -> ``` -> Pro nahrání na opravdovou PyPI stačí vynechat `-r testpypi`. -> Zabírat jména na opravdové PyPI jen tak není hezké vůči ostatním Pythonistům; -> registrujte tedy prosím jen balíčky, které budou nějak pro ostatní užitečné. - - -Instalace pomocí pip --------------------- - -Projekt nahraný na PyPI by mělo jít nainstalovat pomocí pipu. -V případě použití ostré verze PyPI stačí k instalaci zadat název balíčku: - -```console -(__venv__)$ python -m pip install <název_balíčku> -``` - -Pokud však použijeme testovací PyPI, je nutné pipu říct, aby balíček hledal tam. -[Postup](https://packaging.python.org/en/latest/guides/using-testpypi/) uvedený v dokumentaci není -v tomto případě nejvhodnější, protože z testovací PyPI vezme jak náš balíček, -tak i případné závislosti, které mohou být zastaralé, rozbité či jinak škodlivé. - -Lepší by bylo, kdyby pip nainstaloval závislosti z ostré PyPI a na testovací -hledal jen náš projekt. Toho se dá docílit přepínačem `--extra-index-url`. - -```console -(__venv__)$ python -m pip install --extra-index-url https://test.pypi.org/pypi <název_balíčku> -``` - -V tomto případě pip nejdřív prohledá ostrou PyPI, a pokud nenajde požadovaný -balíček, použije testovací PyPI. Zde je potřeba dávat pozor na název projektu, -protože případné konflikty mezi ostrou a testovací PyPI se nekontrolují. -Pokud tedy máme projekt na testovací PyPI a na ostré existuje projekt se -stejným názvem, nainstaluje se ten z ostré verze. - -V případě, že tento problém nastane, je možné ho částečně obejít specifikací -verze instalovaného balíčku: - -```console -(__venv__)$ python -m pip install --extra-index-url https://test.pypi.org/pypi <název_balíčku>==0.3 -``` - -Pokud u duplicitního projektu na ostré PyPI neexistuje požadovaná verze, -nainstaluje se náš balíček z testovací PyPI. - -Jiná možnost je zadat přímo cestu k archivu s balíčkem místo jeho názvu. -Zde pak na umístění balíčku ani verzi nezáleží: - -```console -(__venv__)$ python -m pip install https://test-files.pythonhosted.org/packages/.../<název_balíčku>-0.3.tar.gz -``` - -Odkaz na archiv se dá najít na informační stránce o našem projektu na PyPI. - - -Datové soubory --------------- - -Některé moduly kromě samotného kódu potřebují i datové soubory. -Například aplikace ve Flasku potřebují *templates*. - -Taková data se dají do balíčku přidat klíčem `tool.setuptools.package_data`: - -```toml -[tool.setuptools.packages] -find = {} - -[tool.setuptools.package-data] -"*" = ["*.html"] -``` - -Dodatečnou dokumentaci, jak konfigurovat přidání těchto souborů, najdete v dokumentaci příslušného build backendu. -[Dokumentace k setuptools](https://setuptools.pypa.io/en/latest/userguide/datafiles.html#data-files-support). - - -Wheel: Binární balíčky ----------------------- - -Zatím jsme se zabývali jen zdrojovými balíčky (`sdist`). -Existují ale i balíčky „zkompilované” – binární (`bdist`, nejčastěji `wheel`). -Když se instaluje zdrojový balíček, pip prvně vytvoří `wheel`, a následně ho rozbalí -na patříčné místo. Binární balíček se už jen rozbalí. - -Z historických důvodů existuje několik různých druhů binárních distribucí, -v současné době je ale důležitý pouze `wheel`: - -```console -(__venv__)$ python -m build -``` - -Výsledek je v souboru `dist/*.whl`. - -Obsah binárního balíčku typu wheel můžete prozkoumat, je to obyčejný ZIP. - -Naše programy jsou zatím platformně nezávislé a ve wheelu, -i když se jmenuje binární, žádné binární soubory nejsou. -To se ale změní, až se budeme zabývat tvorbou modulů v jazyce C: -`sdist` pak obsahuje zdrojové soubory a `wheel` zkompilované moduly. - -Potom je dobré distribuovat oba dva – každý má své výhody: - -- *sdist* jde nainstalovat na různých operačních systémech i procesorových - architekturách, -- *sdist* tradičně obsahuje soubory jako LICENSE a README, ale -- *wheel* při instalaci nepotřebuje např. překladače C (všechno už je přeložené - pro konkrétní OS a architekturu), a -- *wheel* se rychleji instaluje. - -Proces vydání složitějšího softwaru pak může vypadat takto: - -```console -(__venv__)$ rm dist/* -(__venv__)$ python -m build -[... kontrola vytvořených balíčků v „čistém“ virtualenvu ...] -(__venv__)$ python -m twine upload dist/* -``` - - -Další ------ - -K balíčkování existuje [obsáhlá dokumentace](https://packaging.python.org/). -Budete-li chtít dělat něco, co v tomto kurzu není, podívejte se tam! diff --git a/lessons/intro/distribution/info.yml b/lessons/intro/distribution/info.yml deleted file mode 100644 index ab2db429..00000000 --- a/lessons/intro/distribution/info.yml +++ /dev/null @@ -1,7 +0,0 @@ -title: Moduly -style: md -attribution: - - Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017 - - Aktualizaci k novému standardu balíčkování provedla Karolina Surma, 2023-2024 -license: cc-by-sa-40 -license_code: cc0 diff --git a/lessons/intro/docs/index.md b/lessons/intro/docs/index.md deleted file mode 100644 index a101d0ff..00000000 --- a/lessons/intro/docs/index.md +++ /dev/null @@ -1,567 +0,0 @@ -Dokumentace -=========== - -Jednou ze zásadních součástí každého kvalitního Python projektu je dokumentace. -Protože chceme, abyste vytvářeli kvalitní projekty, podíváme se tedy i na -dokumentaci. - -Sphinx ------- - -Nejpoužívanějším nástrojem na vytváření dokumentace Python projektů je [Sphinx]. -Když jste se dívali do dokumentace Flasku, requests, clicku, flexmocku, pytestu, -betamaxu či Pythonu samotného, viděli jste dokumentaci vytvořenou ve Sphinxu. - -[Sphinx]: http://www.sphinx-doc.org/ - -Pro vytvoření základní kostry dokumentace se používá jednoduchý průvodce, -`sphinx-quickstart`. - -Postupujte podle následující ukázky. Jsou v ní zobrazeny jen věci, -kde nestačí nechat výchozí hodnota; u ostatních otázek (dostanete-li je) -stačí výchozí hodnotu potvrdit (<kbd>Enter</kbd>). - -```ansi -␛[36m$␛[0m . __venv__/bin/activate -␛[36m(__venv__) $␛[0m python -m pip install sphinx -␛[36m(__venv__) $␛[0m mkdir docs -␛[36m(__venv__) $␛[0m cd docs -␛[36m(__venv__) $␛[0m sphinx-quickstart -␛[01mWelcome to the Sphinx 1.8.1 quickstart utility.␛[39;49;00m - -Please enter values for the following settings (just press Enter to -accept a default value, if one is given in brackets). - -␛[01mSelected root path: .␛[39;49;00m - -You have two options for placing the build directory for Sphinx output. -Either, you use a directory "_build" within the root path, or you separate -"source" and "build" directories within the root path. -␛[35m> Separate source and build directories (y/n) [n]: ␛[39;49;00m - -The project name will occur in several places in the built documentation. -␛[35m> Project name: ␛[39;49;00mcoolthing -␛[35m> Author name(s): ␛[39;49;00mPythonista Dokumentarista -␛[35m> Project release []: ␛[39;49;00m 0.1 - -... - -␛[01mFinished: An initial directory structure has been created.␛[39;49;00m -``` - -Průvodce vytvoří ve složce `docs` několik souborů: - -* `conf.py` – konfigurační soubor, -* `index.rst` – vlastní text dokumantace, -* `Makefile`, `make.bat` – spouštěcí soubory, -* `_static` – adresář na obrázky, CSS apod., -* `_templates` – Adresář na vlastní šablony, -* `_build` – adresář pro výstup, tedy hotovou dokumentaci. - -Do gitu patří všechny nyní vytvořené soubory, kromě složky `docs/_build`, -která by měla být ignorována. - -Zatím se nebudeme zabývat obsahem těchto souborů, ale zkusíme základní kostru -dokumentace sestavit do HTML. - -> [note] -> Sphinx umí generovat dokumentaci ve více formátech (LaTeX, -> manuálové stránky atd.), pro nás bude podstatné především HTML. - -```console -(__venv__) $ make html -... -The HTML pages are in _build/html. -``` - -Ve zmíněné složce byste měli najít `index.html`, ten si můžete prohlédnout -v prohlížeči. - -Textový obsah v dokumentaci ---------------------------- - -Text dokumentace začíná v souboru `index.rst` a píše se ve značkovacím formátu -[reStructuredText] neboli rst. -Ten se od Markdownu liší v syntaxi, která je komplikovanější na -psaní, ale umožňuje dělat komplexnější věci. - -> [note] -> Dokumentaci [lze psát i ve formátu Markdown][sphinx-md], -> ale tato možnost je poměrně nová. -> Jazyk Markdown nebyl navržen pro složitější strukturované texty -> a nepodporuje přímo všechny možnosti, které Sphinx nabízí. -> V dokumentaci se tak dočtete, jak chybějící možnosti doplnit -> [vložením reStructuredText do Markdownového dokumentu][eval_rst]. -> My budeme používat reStructuredText. - -Pro přehled o tom, co reStructuredText umí a jakou má syntaxi, -můžete použít [přehled] z dokumentace Sphinxu, případně [tahák]. - -[reStructuredText]: http://www.sphinx-doc.org/en/stable/rest.html -[přehled]: http://www.sphinx-doc.org/en/stable/rest.html -[tahák]: https://github.com/ralsina/rst-cheatsheet - -[sphinx-md]: https://www.sphinx-doc.org/en/master/usage/markdown.html -[eval_rst]: https://recommonmark.readthedocs.io/en/latest/auto_structify.html#embed-restructuredtext - -V `index.rst` je seznam kapitol: - -```rst -.. toctree:: - :maxdepth: 2 - :caption: Contents: -``` - -Tam můžete přidat další kapitoly: - -```rst -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - intro - tutorial/foo - tutorial/bar - ... -``` - -Soubory s kapitolami je třeba vytvořit ve složce `docs` s příponou `.rst` -(např. `tutorial/foo.rst`). -Text lze pak přidávat samozřejmě do těchto souborů i do -`index.rst`. - -Chcete-li odkazovat na některou sekci, označíme si ji pomocí `.. _label:`: - -```rst -.. _my-reference-label: - -Section to cross-reference --------------------------- - -This is the text of the section. -``` - -Poté na ni lze odkazovat odkudkoli z dokumentace pomocí -[konstrukce ref]: - -```rst -It refers to the section itself, see :ref:`my-reference-label`. -It could refer to a different section as well :) -``` - -[konstrukce ref]: http://www.sphinx-doc.org/en/master/markup/inline.html#role-ref - - -Co do dokumentace psát ----------------------- - -Teď, když víte jak něco napsat, pojďme si povědět *co* vlastně psát. -K čemu dokumentace vlastně je? - -Dobrá dokumentace vysvětluje, proč a jak by váš projekt měl někdo používat. -Jak říká Eric Holscher v [jedné své prezentaci](http://www.writethedocs.org/guide/writing/beginners-guide-to-docs/), - -> Když lidi neví, že váš projekt existuje,<br> -> nebudou ho používat.<br> -> Když lidi nepřijdou na to, jak váš projekt nainstalovat,<br> -> nebudou ho používat.<br> -> Když lidi nepřijdou na to, jak váš projekt použít,<br> -> nebudou ho používat.<br> - -Pokud pracujete v malém týmu, teoreticky jde to všechno kolegům prostě říct, -ale potom se musíte spoléhat na to, že to nezapomenete (a neodejdete z týmu). -Mnohem lepší je dokumentaci sepsat co nejdřív, dokud máte všechno čerstvě -v hlavě. - -Nechce-li se vám nastavovat Sphinx, můžete informace napsat aspoň do malého -README. Ale i tam by měl být stejný druh informací jako ve „velké“ dokumentaci. - -Na první stránce dokumentace (nebo v README) typicky najdeme: - -* krátký text o tom, co projekt dělá; -* ukázku – u knihovny příklad kódu, u aplikace screenshot, u webové aplikace - odkaz na běžící instanci; -* návod na instalaci; -* odkazy na zbytek dokumentace; -* odkazy pro přispěvatele – kde je repozitář, kde nahlásit chybu; -* licenci. - -Delší dokumentace knihoven pak většinou obsahuje: - -* tutoriál – návod, který uživatele provede použitím a možnostmi knihovny; -* popis architektury, návrhu, použitých konceptů; -* dokumentaci API – popis všech veřejných modulů, tříd, funkcí a podobně; -* podrobný návod jak přispívat. - - -Nastavení a rozšíření ---------------------- - -Průvodce `sphinx-quickstart` generuje soubor s nastavením, `conf.py`, -ve kterém můžete měnit nastavení Sphinxu a jeho rozšíření, včetně detailů -jako jméno a verze projektu. - -Průvodce automaticky aktivuje tři rozšíření, která jsou obecně užitečná. -To se ale může v jiných verzích Sphinxu měnit, proto teď nastavení -zkontrolujte a případně rozšíření doplňte: - -```python -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', -] -``` - - -doctest -------- - -`doctest` je modul ze standardní knihovny, který najde v dokumentaci bloky kódu -a otestuje, jestli odpovídají ukázanému výstupu. -Rozšíření `sphinx.ext.doctest` integruje `doctest` do dokumentů -ve formátu reStructuredText. - -Pro nás to bude způsob, jak testovat *dokumentaci* – tedy jestli jsou ukázky -kódu v ní stále platné. -Dá se sice použít i k testování samotného kódu, ale na to existují -[lepší nástroje]({{lesson_url('intro/testing')}}). - -Můžete to dělat dvěma způsoby. První je mít v dokumentaci -příklad vypadající jako interaktivní konzole. -Takový příklad nemusí být odsazený ani ničím uvozený; stačí `>>>` na začátku. - -```pycon ->>> 1 + 1 -2 -``` - -Doctest v tomto případě otestuje, že vše funguje, jak má. -V tomto případě se provede součet a zkontroluje se, zda výsledek je 2. - -Druhý způsob je mít v dokumentaci nejdříve kód: - -```python -print('foo') -``` - -A dále někde jinde výstup volání: - -```python -foo -``` - -K tomu všemu složí několik direktiv: - -### .. testsetup:: - -Direktiva pro potřebný kód, který se musí provést, aby příklad fungoval, ale -nebude v dokumentaci zobrazen (např. kód pro vytvoření falešného objektu, -import...). - -### .. testcleanup:: - -Podobná direktiva jako `.. testsetup::` provedená po skončení testů. -V dokumentaci nebude kód zobrazen. - -### .. doctest:: - -Test s interaktivní konzolí. V dokumentaci bude zobrazen, pokud nepoužijete flag -`:hide:`. - -### .. testcode:: - -Kód testu bez interaktivní konzole, co chcete kontrolovat, musíte dát na -standardní výstup. V dokumentaci bude zobrazen, pokud nepoužijete flag -`:hide:`. - -### .. testoutput:: - -Výstup posledního testcode bloku. V dokumentaci bude kód zobrazen, pokud -nepoužijete flag `:hide:`. - -### Kompletní příklad - -Zde můžete vidět výše zmíněné direktivy použité dohromady. -Jedná se o umělý příklad, kdy použitou třídu připravíme v direktivě `testsetup`. -V praxi pak doctestem testujeme, jestli naše dokumentace odpovídá chování -naší implementace, třídu `Parrot` bychom tedy odněkud naimportovali. - -```rst -The parrot module -================= - -.. testsetup:: - - class Parrot: - def voom(self, voltage): - print('This parrot wouldn\'t voom if you put {} volts through it!'.format(voltage)) - - def die(self): - return 'RIP' - - - parrot = Parrot() - -The parrot module is a module about parrots. - -Doctest example: - -.. doctest:: - - >>> parrot.voom(3000) - This parrot wouldn't voom if you put 3000 volts through it! - -Test-Output example: - -.. testcode:: - - parrot.voom(3000) - -This would output: - -.. testoutput:: - - This parrot wouldn't voom if you put 3000 volts through it! - -You can use other values: - -.. testcode:: - - parrot.voom(230) - -.. testoutput:: - :hide: - - This parrot wouldn't voom if you put 230 volts through it! - - -.. testcleanup:: - - parrot.die() -``` - -Testy se také dají např. zařazovat do skupin. Více najdete -v [dokumentaci](http://www.sphinx-doc.org/en/master/ext/doctest.html). - -```console -(__venv__) $ make doctest -... -Document: intro ---------------- -1 items passed all tests: - 3 tests in default -3 tests in 1 items. -3 passed and 0 failed. -Test passed. -1 items passed all tests: - 1 tests in default (cleanup code) -1 tests in 1 items. -1 passed and 0 failed. -Test passed. - -Doctest summary -=============== - 3 tests - 0 failures in tests - 0 failures in setup code - 0 failures in cleanup code -... -``` - -### Import z vlastního kódu - -Pokud nemáte nainstalovaný vlastní balíček a budete z něj chtít v doctestu -importovat, pravděpodobně dostanete `ImportError`. -V takovém případě pomůže drobná editace na začátku `conf.py`. -Musíte přidat adresář, ze kterého lze váš kód importovat, do `sys.path`. -Pokud jste postupovali podle návodu výše, máte dokumentaci v adresáři `docs`, -je tedy potřeba přidat nadřazený adresář (`..`): - -```python -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys -sys.path.insert(0, os.path.abspath('..')) -``` - -### Travis CI - -Neexistuje zatím unifikovaný způsob, jak specifikovat závislosti pro sestavení -dokumentace. Proto, pokud chcete mít nějaký jednoduchý způsob, jak pouštět -doctesty na Travisu, vytvořte například soubor `docs/requirements.txt` -a do něj dejte závislosti potřebné pro sestavení dokumentace. -Je na vás, jestli tam budou pouze extra závislosti oproti těm v `setup.py` -(většinou pouze `sphinx`), nebo všechny závislosti, aby šel použít soubor -samostatně. - -Poté na Travisu můžete udělat něco jako: - -```yaml -language: python -python: -- '3.7' -install: -- python setup.py install -- pip install -r docs/requirements.txt -script: -- python setup.py test --addopts -v -- cd docs && make doctest -``` - -> [note] -> Chcete-li jít s dobou, můžete vyzkoušet strukturovaný způsob závislostí -> pro vývoj pomocí *extras*. Aktuálně pro to neexistuje standard, -> ale vypadá to, že následující způsob je nejlepší kandidát na -> standardizaci. -> -> Do `setup.py` přidejte: -> -> ```python -> extras_require={ -> 'dev': ["sphinx"], -> } -> ``` -> -> Projekt pak lze nainstalovat pomocí `.[dev]` (tedy jméno balíčku a za ním -> jméno *extras* v hranatých závorkách): -> -> ``` -> install: -> - python -m pip install .[dev] -> ``` - - -autodoc -------- - -Pro dokumentaci API lze použít `sphinx.ext.autodoc`, další rozšíření Sphinxu, -které průvodce přidává automaticky. - -> [note] -> Nemáte-li toto rozšíření povolené, přidejte jej do `conf.py`: -> -> ```python -> extensions = [ -> 'sphinx.ext.autodoc', -> 'sphinx.ext.doctest', -> 'sphinx.ext.intersphinx', -> ] -> ``` - -Rozšíření `autodoc` se používá takto: - -```rst -.. automodule:: mymodule - :members: -``` - -Tento příklad na dané místo vygeneruje dokumentaci složenou z dokumentačních -řetězců jednotlivých funkcí, tříd a metod v modulu `mymodule`. - -Pokud chcete selektivně vybrat, dokumentaci čeho chcete generovat, -můžete použít i -[jiné direktivy](http://www.sphinx-doc.org/en/master/ext/autodoc.html#directive-automodule). - -Pro vygenerování hezké struktury si můžete pomoci příkazem `sphinx-apidoc`: - -```console -(__venv__) $ sphinx-apidoc -o docs mymodule -``` - -V dokumentačních řetězcích samozřejmě můžete použít [reStructuredText] a je to -dokonce žádoucí. - -Zde je ukázka z betamaxu (*Copyright 2013 Ian Cordasco*): - -```python -class Betamax: - - """This object contains the main API of the request-vcr library. - - This object is entirely a context manager so all you have to do is: - - .. code:: - - s = requests.Session() - with Betamax(s) as vcr: - vcr.use_cassette('example') - r = s.get('https://httpbin.org/get') - - Or more concisely, you can do: - - .. code:: - - s = requests.Session() - with Betamax(s).use_cassette('example') as vcr: - r = s.get('https://httpbin.org/get') - - This object allows for the user to specify the cassette library directory - and default cassette options. - - .. code:: - - s = requests.Session() - with Betamax(s, cassette_library_dir='tests/cassettes') as vcr: - vcr.use_cassette('example') - r = s.get('https://httpbin.org/get') - - with Betamax(s, default_cassette_options={ - 're_record_interval': 1000 - }) as vcr: - vcr.use_cassette('example') - r = s.get('https://httpbin.org/get') - """ -``` - -Existují různé způsoby, jak dokumentovat argumenty, návratové hodnoty apod. -Zvídavým studentům doporučujeme podívat se na rozšíření [Napoleon]. - -[Napoleon]: http://www.sphinx-doc.org/en/master/ext/napoleon.html - - -Odkazy na třídy a moduly ------------------------- - -Máte-li zdokumentovaný modul, funkci, třídu, metodu apod., je možné na ni -odkázat pomocí konstrukce `:mod:`, `:func:`, `:class:`, `:meth:` a dalších -ze Sphinxové [domény Python]: - -```rst -To test the parrot's electrical resistance, use :meth:`parrot.voom()`. -``` - -V této části dokumentace Sphinxu též najdete způsob, jak dokumentovat API -bez použití `autodoc`. - -Všechny zdokumentované objekty se automaticky přidávají do rejstříku. -Chcete-li do rejstříku přidat něco navíc, použijte direktivu [index]. - -[domény Python]: http://www.sphinx-doc.org/en/master/domains.html#cross-referencing-python-objects -[index]: http://www.sphinx-doc.org/en/master/markup/misc.html#index-generating-markup - - -README.rst ----------- - -Když už se stejně zabýváme [reStructuredText]em, je dobré váš README přepsat -nebo převést do stejného formátu. Na PyPI pak bude váš projekt vypadat lépe. - -Při přejmenování na `README.rst` dejte pozor na patřičné změny v `setup.py`. - -Read the Docs -------------- - -Pokud svůj repositář na GitHubu změníte na veřejný, můžete využít službu -[Read the Docs] k hostování dokumentace ve Sphinxu. -Dokumentace se sestaví při každém pushnutí na GitHub. - -Pokud Read the Docs použijete, nezapomeňte na dokumentaci odkázat -z `README.rst`. - -[Read the Docs]: https://readthedocs.org/ diff --git a/lessons/intro/docs/info.yml b/lessons/intro/docs/info.yml deleted file mode 100644 index ea991b72..00000000 --- a/lessons/intro/docs/info.yml +++ /dev/null @@ -1,5 +0,0 @@ -course: MI-PYT -title: Dokumentace -style: md -attribution: Pro kurz MI-PYT na ČVUT napsali Miro Hrončok, Petr Viktorin a další, 2016-2017. -license: cc-by-sa-40 diff --git a/lessons/intro/flask/index.md b/lessons/intro/flask/index.md deleted file mode 100644 index da2b7618..00000000 --- a/lessons/intro/flask/index.md +++ /dev/null @@ -1,550 +0,0 @@ -Webové aplikace: Flask -====================== - -Python je víceúčelový jazyk. -Na minulém cvičení jsme tvořili aplikace pro příkazovou řádku, -nyní se podíváme na aplikace webové. - -Webových frameworků pro Python je více, mezi nejznámější patří [Django], -[Flask] nebo [Pyramid]. - -Pro naše účely použijeme [Flask], protože je nejrychlejší na pochopení a -nevyžaduje striktně použití [MVC] paradigmatu. - -[Django]: https://www.djangoproject.com/ -[Flask]: https://flask.palletsprojects.com -[Pyramid]: http://www.pylonsproject.org/ -[MVC]: https://cs.wikipedia.org/wiki/Model-view-controller - -Flask ------ - -Flask opět můžete nainstalovat do virtualenvu, nejlépe použít projekt -z minulého cvičení: - -```console -$ cd project -$ . __venv__/bin/activate -(__venv__) $ python -m pip install Flask -``` - -Základní použití Flasku je poměrně primitivní. -Do souboru `hello.py` napište: - -```python -from flask import Flask -app = Flask(__name__) - -@app.route('/') -def index(): - return 'MI-PYT je nejlepší předmět na FITu!' - -``` - -Pak aplikaci spusťte pomocí následujících příkazů. -(Na Windows použijte místo `export` příkaz `set`.) - -```console -(__venv__) $ export FLASK_APP=hello.py -(__venv__) $ export FLASK_DEBUG=1 -(__venv__) $ flask run - * Serving Flask app "hello" - * Forcing debug mode on - * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) - * Restarting with stat - * Debugger is active! - * Debugger PIN: 189-972-345 -``` -Na zmíněné adrese byste měli v prohlížeči vidět použitý text. - -Proměnná prostředí `FLASK_APP` říká Flasku, kde aplikaci najít. -V daném souboru Flask hledá automaticky proměnnou jménem `app`. -([Jde nastavit](https://flask.palletsprojects.com/en/1.1.x/cli/) i jiná.) -Proměnná `FLASK_DEBUG` nastavuje ladícím režim, který si popíšeme za chvíli. - -V programu jsme jako `app` vytvořili flaskovou aplikaci. -Argument `__name__` je jméno modulu – Flask podle něj hledá soubory, -které k aplikaci patří (viz `static` a `templates` níže). - -Pomocí dekorátoru [`@app.route`] jsme zaregistrovali takzvaný *view* (pohled) – -funkci, která vrací obsah pro danou [cestu v URL][URL]. -Tomuto spojení cesty a pohledové funkce se říká *route* (nebo počeštěně „routa“). -My konkrétně říkáme, že na cestě `/` (tedy na „domovské stránce“) bude -k dispozici obsah, který vrátí funkce `index`. - -[URL]: ../../fast-track/http/#url-anatomy - -Více různých adres lze obsloužit jednoduše přidáním dalších funkcí: - -```python -@app.route('/') -def index(): - return 'Index Page' - -@app.route('/hello/') -def hello(): - return 'Hello, World' -``` - -Na adrese [`http://127.0.0.1:5000/hello/`][local-hello] pak uvidíte druhou stránku. - -[`@app.route`]: https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.route -[local-hello]: http://127.0.0.1:5000/hello/ - -### Ladící režim - -Proměnná `FLASK_DEBUG` říká, že se aplikace má spustit v ladícím režimu: -je zapnutý příjemnější výpis chyb a aplikace se automaticky restartuje -po změnách. - -Zkuste ve funkci `hello()` vyvolat výjimku (například dělení nulou – `1/0`) -a podívat se, jak chyba v ladícím režimu „vypadá“: -Flask ukáže *traceback* podobný tomu z příkazové řádky a navíc vám na každé -úrovni umožní pomocí malé ikonky spustit konzoli. -Bezpečnostní PIN k této konzoli najdete v terminálu, kde server běží. - -Ladící režim je užitečný, ale nebezpečný – návštěvníkům stránky může -(po prolomení celkem jednoduchého „hesla“) umožnit spustit jakýkoli pythonní kód. -Navíc aplikaci zpomaluje. -Používejte ho proto *pouze* na svém počítači. - -### Dynamické routy - -Když vytváříte dynamický web, ne vždy můžete všechna URL znát dopředu. -Budete například chtít zobrazit informace o uživatelích na adresách -jako `/user/hroncok/`, ale nemůžete při každé registraci nového uživatele -přidávat novou funkci do kódu. -Musíte použít [dynamické routy]: - -[dynamické routy]: https://flask.palletsprojects.com/en/1.1.x/quickstart/#variable-rules - -```python -@app.route('/user/<username>/') -def profile(username): - return 'User {}'.format(username) -``` - -Proměnnou část cesty ohraničíte lomenými závorkami a použijte jako parametr -funkce. Pokud chcete, můžete specifikovat, na jaký obsah se pravidlo vztahuje. -Například číselný identifikátor článku pro adresy jako `/post/42/` můžete zadat -takto: - -```python -@app.route('/post/<int:post_id>/') -``` - -Můžete použít různá pravidla, např.: - - * `string` akceptuje jakýkoliv text bez lomítek (výchozí) - * `int` akceptuje celá čísla (a pohledové funkci je předá jako `int`, ne text) - * `float` akceptuje i desetinná čísla s tečkou (a předá je jako `float`) - * `path` akceptuje text i s lomítky - -Rout můžete definovat i víc pro jednu funkci. -Často se to používá s výchozí hodnotou argumentu: - -```python -@app.route('/hello/') -@app.route('/hello/<name>/') -def hello(name='world'): - return 'Hello, {}!'.format(name) -``` - -### Získání URL - -Opačným způsobem jak k routám přistupovat je, když potřebujete získat URL -nějaké stránky, například protože potřebujete zobrazit odkaz. -K tomu se používá funkce [`url_for()`], která jako první parametr bere jméno -routy (neboli jméno funkce, která routu obsluhuje), a pak pojmenované argumenty -pro pravidla v dynamické routě: - -[`url_for()`]: https://flask.palletsprojects.com/en/1.1.x/api/#flask.url_for - -```python -from flask import url_for - -... - -@app.route('/url/') -def show_url(): - return url_for('profile', username='hroncok') -``` - -Tuto funkci jde použít jen uvnitř pohledové funkce, -Pokud ji chcete vyzkoušet například v interaktivní konzoli, -můžete použít speciální kontext: - -```pycon ->>> with app.test_request_context(): -... print(url_for('profile', username='hroncok')) -... -/user/hroncok/ -``` - -Možná si říkáte, proč tu URL prostě nevytvořit ručně. -S takovým přístupem byste ale mohli narazit na problém, pokud cestu později -změníte – což se může stát např. i když web nasadíte na jiný server. -Generování URL vám také může zjednodušit nasazení statické verze stránek. - -Pro URL v rámci vašich stránek proto doporučujeme `url_for` používat důsledně. - -### Šablony - -Zatím jsou naše webové stránky poměrně nudné: obsahují jen prostý text, -nepoužívají HTML. - -> [note] -> Předpokládáme, že víte co je to [HTML] a [CSS]. -> Jestli ne, doporučujeme si projít základy těchto webových technologií -> např. na stránkách [MDN]. - -[HTML]: https://developer.mozilla.org/en-US/docs/Web/HTML -[CSS]: https://developer.mozilla.org/en-US/docs/Web/CSS -[MDN]: https://developer.mozilla.org/en-US/docs/Web - -Klidně byste mohli udělat něco jako: - -```python -@app.route('/') -def hello(): - return '<html><head><title>...' -``` - -...ale asi by to nebylo příliš příjemné. -Python je jazyk dělaný na popis algoritmů, procesů a logiky spíš než obsahu. -Lepší je HTML dát do zvláštního souboru a použít ho jako *šablonu* -(angl. *template*). -Z Flasku vypadá použití šablony takto: - -```python -from flask import render_template - -@app.route('/hello/') -@app.route('/hello/<name>/') -def hello(name=None): - return render_template('hello.html', name=name) -``` - -Funkce `render_template` nejen vrátí HTML z daného souboru, ale umí do něj -i doplnit informace, které dostane v pojmenovaných argumentech. - -Ukažme si to na příkladu: vedle souboru s kódem vytvořte složku `templates` -a v ní `hello.html` s tímto obsahem: - -{% raw %} -```html+jinja -<!doctype html> -<html> - <head> - <title>Hello from Flask - - - {% if name %} -

Hello {{ name }}!

- Go back home - {% else %} -

Hello, World!

- {% endif %} - - -``` -{% endraw %} - -{% raw %} -Šablony používají (jako výchozí variantu) šablonovací jazyk [Jinja2], -který se s Flaskem a jinými frameworky pro Python používá často. -Kompletní popis jazyka najdete v [dokumentaci][Jinja2], ale -pro většinu stránek se obejdete s doplněním hodnoty (`{{ promenna }}`) -a podmíněným obsahem (`{% if %}`) jako výše, -případně s [cyklem][jinja-for]: `{% for %}`/`{% endfor %}`. Ve větších -aplikacích se pak hodí použití `{% include ... %}`, `{% extends ... %}` -a případně také tvorba maker `{% macro ... %}`/`{% endmacro %}`. -{% endraw %} - -Veškerý kontext (proměnné) do šablony musí přijít z volání `render_template()`. -Navíc můžete použít vybrané funkce, např. `url_for()`. -(Jiné funkce známé z Pythonu ale použít nejdou – ač jsou podobné, je Jinja2 -jiný jazyk než Python.) - -[Jinja2]: https://jinja.palletsprojects.com/en/2.10.x/templates/ -[jinja-for]: https://jinja.palletsprojects.com/en/2.10.x/templates/#for - -#### Filtry - -Není úplně elegantní vzít nějaká data (např. tweety z Twitter API) a ještě před -předáním šabloně do nich cpát svoje úpravy (např. převod na HTML). -Od toho jsou tu filtry. Filtr transformuje hodnotu na řetězec, -který pak ukážeme uživateli. - -Zde je například filtr `time`, který načte čas v určitém formátu -a převede ho do jiného: - -```python -from datetime import datetime - -@app.template_filter('time') -def convert_time(text): - """Convert the time format to a different one""" - dt = datetime.strptime(text, '%a %b %d %H:%M:%S %z %Y') - return dt.strftime('%c') - -@app.route('/date_example') -def date_example(): - return render_template( - 'date_example.html', - created_at='Tue Mar 21 15:50:59 +0000 2017', - ) -``` - -V šabloně `date_example.html` se pak filtr použije pomocí svislítka: - -{% raw %} -```html+jinja -{{ created_at|time }} -``` -{% endraw %} - -Pokud potřebujete velmi obecný filtr, je vhodné se podívat do [seznamu těch vestavěných](https://jinja.palletsprojects.com/en/2.10.x/templates/#builtin-filters). - -#### Escaping - -V textu, který se vkládá do šablon, jsou automaticky nahrazeny znaky, které -mají v HTML speciální význam. -Zabraňuje se tak bezpečnostním rizikům, kdy se vstup od uživatele interpretuje -jako HTML. - -Například když v aplikaci výše navštívíme URL `/hello/