diff --git a/courses/pyladies/info.yml b/courses/pyladies/info.yml index 91720b809f..f29c51834c 100644 --- a/courses/pyladies/info.yml +++ b/courses/pyladies/info.yml @@ -100,10 +100,17 @@ plan: 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: Iterujeme + slug: iteration + materials: + - lesson: beginners/range + - lesson: beginners/zip-enumerate + - title: Grafika slug: pyglet materials: diff --git a/lessons/beginners/list/index.md b/lessons/beginners/list/index.md index f51b69c8a2..06345f6f0c 100644 --- a/lessons/beginners/list/index.md +++ b/lessons/beginners/list/index.md @@ -1,27 +1,17 @@ -Tahle kapitola je plná nových věcí. -Doufám, že vydržíš až do konce. A kdyby něco -zatím nedávalo úplně smysl, nevěš hlavu: -věci, které si teď vysvětlíme, se opravdu naučíš -až v dalších lekcích, kde je budeme využívat -prakticky. - 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 doporučuji vytisknout. - -Každý příklad v tomto textu si vyzkoušej; -to, co Python vypíše, je důležitá součást lekce, -i když v materiálech není přímo napsaná. +který si můžeš vytisknout. # Seznamy Dnes si ukážeme, jak pracovat se *seznamy* (angl. *lists*). -Doufám, že víš, kde máš na klávesnici hranaté -závorky, protože právě těmi se seznamy vytváří: + +Začneme prakticky. +Vytvoř si seznam pomocí následujícího kódu: ```python -cisla = [1, 1, 2, 3, 5, 8, 13] -print(cisla) +zviratka = ['pes', 'kočka', 'králík'] +print(zviratka) ``` > [note] @@ -30,204 +20,253 @@ print(cisla) Seznam je hodnota, která může obsahovat spoustu dalších hodnot. Tak jako řetězec obsahuje sekvenci znaků, -seznam obsahuje sekvenci... čehokoliv. Třeba čísel. -A tak jako můžeme pomocí cyklu `for` -procházet řetězec po znacích, -seznam můžeme procházet po jednotlivých prvcích: +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 cislo in cisla: - print(cislo) +for zviratko in zviratka: + print(zviratko) ``` -Seznamy se v programech vyskytují velice často: -soubor se dá načíst jako seznam řetězců -s jednotlivými řádky, -seznam řetězců jako `'7♥'` -a `'K♣'` může posloužit jako balíček karet, -matematika je plná číselných řad, -každá online služba má seznam uživatelů. +Seznamy se v programech vyskytují často: +soubor se dá načíst jako seznam řetězců s jednotlivými řádky, +seznam řetězců jako `'7♥'` a `'K♣'` může posloužit jako balíček karet, +matematika je plná číselných řad, e-shopy pracují se seznamy zboží. + +Hodnoty v seznamu můžou být jakéhokoli typu: +```python +prvni_prvocisla = [2, 3, 5, 7, 11] +``` -Hodnoty v seznamu můžou být jakéhokoli typu, -dokonce můžeme různé typy míchat v jednom seznamu -(i když s takovými namixovanými seznamy se -příliš často nesetkáme – více se používají v -n-ticích, o kterých si povíme později): +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) ``` -## Vybírání ze seznamů +## Neměnitelné hodnoty + +Důležitá vlastnost seznamů je, že se dají *měnit*. -Nejzákladnější operaci se seznamy, -cyklus `for`, už jsme si ukázal{{gnd('i', 'y', both='i')}}. -Druhá nejdůležitější operace je vybírání -jednotlivých prvků. -To funguje jako u řetězců: do hranatých závorek -se dá číslo prvku. Čísluje se, jako u řetězců, -od nuly; záporná čísla označují prvky od konce. +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 -print(cisla[2]) +kamaradka = 'Žaneta' +print(kamaradka) +kamaradka.upper() +print(kamaradka) ``` -Hranatými závorkami můžeme získávat podseznamy. -[Diagram z materiálů k řetězcům]({{ lesson_url('beginners/str')}}#slicing-diagram) -ukazuje, jak u takového „sekání” číslovat: -funguje to stejně, jen místo menšího řetězce -dostaneme menší seznam. +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 vypočítá, +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 -print(cisla[2:-3]) +kamaradka = kamaradka.upper() ``` -## Měnění seznamů +Tímto přiřazením Python „zahodí“ původní hodnotu, +a od tohoto příkazu dál bude proměnná `kamaradka` označovat nový řetězec. -Důležitá vlastnost seznamů, kterou nemají ani čísla, ani řetězce -(a `True`/`False`/`None` už vůbec ne), je, -že seznamy se dají měnit. +Podobně by se dala proměnná přenastavit na jakoukoli jinou hodnotu: -Čísla měnit nejdou – máš-li `a = 3` a -napíšeš `a = a + 1`, číslo `3` se nezmění. -Vypočítá se nové číslo `4` a proměnná `a` -se nastaví na toto nové číslo. +```python +kamaradka = 'Žaneta' +print(kamaradka) +kamaradka = 'Alexandra' +print(kamaradka) +``` + + +## Měnění seznamů + +A jak jsou na tom seznamy? +Ty se měnit dají. -Oproti tomu seznamy se dají měnit bez nastavování proměnné. 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, na kterém pracuje. Vyzkoušej si to: +Ta *nic nevrací* (resp. vrací `None`), ale „na místě” (angl. *in place*) změní +seznam, se kterým pracuje. Vyzkoušej si to: -```python -prvocisla = [2, 3, 5, 7, 11, 13, 17] -print(prvocisla) -prvocisla.append(19) -print(prvocisla) +```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'] ``` -Takové měnění hodnoty může být občas překvapující, -protože stejnou hodnotu může mít více proměnných. +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 proměnná „mění aniž na ni sáhneme”: +ž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áří +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 pracujeme jenom s jedním seznamem +# 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, existuje metoda `extend`, -která umí přidávat prvků víc. -Prvky k přidání jí předáme ve formě seznamu: +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, +* `insert()` přidá prvek na danou pozici, +* `pop()` odebere poslední prvek a *vrátí ho* (jako návratovou hodnotu), +* `remove()` odstraní první výskyt daného prvku, +* `sort()` seznam seřadí (řetězce podle “abecedy”, čísla vzestupně), +* `reverse()` obrátí pořadí prvků, +* `clear()` odstraní všechny prvky. + +{{ figure(img=static('methods.svg'), alt="Tahák") }} + +Například: ```python -dalsi_prvocisla = [23, 29, 31] -prvocisla.extend(dalsi_prvocisla) -print(prvocisla) +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() # [] ``` -Metoda `extend` umí pracovat i s jinými -typy než se seznamy – ráda zpracuje cokoli, přes -co umí cyklit `for`: např. -jednotlivé znaky řetězců, řádky souborů, nebo čísla z `range()`. +## 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 -seznam = [] -seznam.extend('abcdef') -seznam.extend(range(10)) -print(seznam) +zviratka = ['pes', 'kočka', 'králík'] +print(zviratka[2]) ``` -## Měnění prvků +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 +print(zviratka[2:-3]) +``` + +„Sekáním“ vzniká nový seznam – když pak ten původní změníš, v novém menším seznamu se +to neprojeví. + -Ale dost přidávání. -Seznamům se dají i měnit jednotlivé prvky -a to jednoduše tak, že do prvku přiřadíme, -jako by to byla proměnná: +### 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 -cisla = [1, 0, 3, 4] -cisla[1] = 2 -print(cisla) +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řazujeme. -Jako u `extend` můžeš do podseznamu opět přiřadit cokoli, co umí -zpracovat `for` – seznam, řetězec, `range()` apod. +co přiřadíš. ```python -cisla = [1, 2, 3, 4] -cisla[1:-1] = [6, 5] -print(cisla) +zviratka = ['pes', 'kočka', 'králík', 'had', 'andulka'] +print(zviratka[1:-1]) +zviratka[1:-1] = ['koťátko', 'králíček', 'hádě'] +print(zviratka) ``` -## Mazání prvků +### Mazání prvků -Přiřazením do podseznamu se dá i změnit délka -seznamu, nebo některé prvky úplně odstranit: +Přiřazením do podseznamu můžeš i změnit délku +seznamu nebo některé prvky úplně odstranit: ```python -cisla = [1, 2, 3, 4] -cisla[1:-1] = [0, 0, 0, 0, 0, 0] -print(cisla) -cisla[1:-1] = [] -print(cisla) +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ý, a proto na to máme zvláštní příkaz -jménem `del`. +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 -cisla = [1, 2, 3, 4, 5, 6] -del cisla[-1] -print(cisla) -del cisla[3:5] -print(cisla) -del cisla -print(cisla) +zviratka = ['pes', 'kočka', 'králík', 'had', 'ještěrka', 'andulka'] + +print(zviratka[-1]) +del zviratka[-1] +print(zviratka) + +print(zviratka[1:-1]) +del zviratka[1:-1] +print(zviratka) + +del zviratka +print(zviratka) ``` -Další mazací metody jsou: -* `pop`, která odstraní *a vrátí* poslední prvek v seznamu – například pokud - mám seznam karet v balíčku, jde takhle jednoduše „líznout” kartu, -* `remove`, která najde v seznamu daný prvek a odstraní ho, -* `clear`, která vyprázdní celý seznam. +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 -cisla = [1, 2, 3, 'abc', 4, 5, 6, 12] -posledni = cisla.pop() -print(posledni) -print(cisla) +balicek = ['eso', 'sedma', 'svršek', 'sedma', 'král'] +liznuta_karta = karty.pop() +print(liznuta_karta) +print(balicek) -cisla.remove('abc') -print(cisla) +balicek.remove('sedma') +print(balicek) -cisla.clear() -print(cisla) +balicek.clear() +print(balicek) ``` ## Řazení -A taky tu máme metodu `sort`, která prvky seznamu seřadí. +Metoda `sort` seřadí prvky seznamu. ```python seznam = [4, 7, 8, 3, 5, 2, 4, 8, 5] @@ -236,16 +275,14 @@ print(seznam) ``` Aby se daly seřadit, musí být prvky seznamu vzájemně -*porovnatelné* – konktrétně na ně musí fungovat -operátor `<`. +*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ě se řadí (např. čísla podle velikosti; -řetězce podle speciální „abecedy” která řadí -velká písmena za malá, česká až za anglická, atd.). +Operátor `<` definuje i jak přesně `sort` řadí: čísla vzestupně podle +velikosti; řetězce podle speciální „abecedy” která řadí +velká písmena za malá, česká až za anglická, atd. -Metoda `sort` zná pojmenovaný argument -`reverse`. Pokud ho nastavíš na *True*, řadí se „naopak”. +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] @@ -255,7 +292,7 @@ print(seznam) ## Známé operace se seznamy -Spousta toho, co můžeme dělat s řetězci, má stejný +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: @@ -264,7 +301,7 @@ melodie = ['C', 'E', 'G'] * 2 + ['E', 'E', 'D', 'E', 'F', 'D'] * 2 + ['E', 'D', print(melodie) ``` -Stejně jako u řetězců, sečítat jde jen seznam +Stejně jako u řetězců jde sečítat jen seznam se seznamem – ne třeba seznam s řetězcem. Další staří známí jsou funkce `len`, @@ -280,8 +317,8 @@ 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 naše melodie obsahuje prvky -`'D'` a `'E'` vedle sebe, `'DE'` v seznamu není: +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) @@ -291,7 +328,7 @@ print(melodie.index('DE')) ## Seznam jako podmínka -Seznam se dá použít v příkazu `if` (nebo `while`) 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`. @@ -302,19 +339,17 @@ else: print('Seznam je prázdný!') ``` -Podobně se dají v podmínce použít i řetězce. +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` (angl. *seznam*) převádí na seznam. -Jako argument jí předáme jakoukoli hodnotu, +funkce `list` převádí na seznam. +Jako argument jí můžeš předat jakoukoli hodnotu, kterou umí zpracovat příkaz `for`. -Z řetězců udělá seznam znaků, z otevřeného souboru -udělá seznam řádků, z `range` udělá -seznam čísel. +Z řetězce udělá seznam znaků, z `range` udělá seznam čísel. ```python abeceda = list('abcdefghijklmnopqrstuvwxyz') @@ -324,8 +359,7 @@ print(cisla) ``` I ze seznamu udělá funkce `list` seznam. -To může znít zbytečně, ale není – vytvoří se -totiž *nový* 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. @@ -337,13 +371,14 @@ 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 chceme mocnit, +mocnin dvou, projdi čísla, kterými chceš mocnit, cyklem `for` a pro každé z nich do seznamu přidej příslušnou mocninu: @@ -354,25 +389,54 @@ for cislo in range(10): print(mocniny_dvou) ``` +Podobným způsobem získáš seznam 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. +zavolej `append` pro všechny kombinace barev a hodnot: ```python balicek = [] -for barva in '♠', '♥', '♦', '♣': # (Na Windows použij textová jména) - for hodnota in list(range(2, 11)) + ['J', 'Q', 'K', 'A']: - balicek.append(str(hodnota) + barva) +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) ``` +> [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í”, -takže snad nepřekvapí, že se dá různě převádět -z jednoho typu na druhý. -Funkce `list` vytvoří z řetězce -seznam znaků. -Když chceme dostat seznam slov, použijeme +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 @@ -381,119 +445,45 @@ print(slova) ``` Metoda `split` umí brát i argument. -Pokud ho předáme, místo mezer (a nových řádků) -se řetězec „rozseká” daným oddělovačem. -Takže když máme nějaká data oddělená čárkami, -není nic jednoduššího než použít `split` s čárkou: +Pokud ho předáš, řetězec „rozseká” daným oddělovačem +(místo mezer a nových řádků). +Takže když máš nějaká data oddělená čárkami, +použíj `split` s čárkou: ```python zaznamy = '3A,8B,2E,9D'.split(',') print(zaznamy) ``` -Chceme-li spojit seznam řetězců zase dohromady -do jediného řetězce, použijeme metodu -`join` (angl. *spojit*). +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 řetězci, kterým se jednotlivé kousky „slepí” -dohromady; a jako argument bere seznam jednotlivých -řetězců. +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) ``` -## Úkol - -Představ si, že ti uživatelé zadávají jména a příjmení a ty si je ukládáš do -seznamu pro další použití např. v evidenci studentů. Ne všichni jsou ale pořádní, -a tak se v seznamu sem tam objeví i jméno s nesprávně zadanými velkými písmeny. -Například: - -```python -zaznamy = ['pepa novák', 'Jiří Sládek', 'Ivo navrátil', 'jan Poledník'] -``` - -Úkolem je: - -* Napsat funkci, která vybere jen ty správně zadané záznamy, které mají správně -jméno i příjmení s velkým počátečním písmenem. -* Napsat funkci, která vybere naopak jen ty nesprávně zadané záznamy. -* *(Nepovinný)* – Napsat funkci, která vrátí seznam s opravenými záznamy. - -Výsledné funkce by měly fungovat takto: - -```python -zaznamy = ['pepa novák', 'Jiří Sládek', 'Ivo navrátil', 'jan Poledník'] - -chybne_zaznamy = vyber_chybne(zaznamy) -print(chybne_zaznamy) # → ['pepa novák', 'Ivo navrátil', 'jan Poledník'] - -spravne_zaznamy = vyber_spravne(zaznamy) -print(spravne_zaznamy) # → ['Jiří Sládek'] - -opravene_zaznamy = oprav_zaznamy(zaznamy) -print(opravene_zaznamy) # → ['Pepa Novák', 'Jiří Sládek', 'Ivo Navrátil', 'Jan Poledník'] -``` - -> [note] -> Snadný způsob jak zjistit, zda je řetězec složen jen z malých písmen, -> je metoda `islower()`, která vrací True, pokud řetězec obsahuje jen malá -> písmena, jinak vrací False. Například `'abc'.islower() == True` ale -> `'aBc'.islower() == False`. -> -> Snadný způsob jak převést první písmenko na velké je metoda `capitalize()`: -> např. `'abc'.capitalize() == 'Abc'` - -{% filter solution %} -```python -def vyber_chybne(seznam): - vysledek = [] - for zaznam in seznam: - jmeno_a_prijmeni = zaznam.split(' ') - jmeno = jmeno_a_prijmeni[0] - prijmeni = jmeno_a_prijmeni[1] - if jmeno[0].islower() or prijmeni[0].islower(): - vysledek.append(zaznam) - return vysledek - -def vyber_spravne(seznam): - vysledek = [] - for zaznam in seznam: - jmeno_a_prijmeni = zaznam.split(' ') - jmeno = jmeno_a_prijmeni[0] - prijmeni = jmeno_a_prijmeni[1] - if not jmeno[0].islower() and not prijmeni[0].islower(): - vysledek.append(zaznam) - return vysledek - -def oprav_zaznamy(seznam): - vysledek = [] - for zaznam in seznam: - jmeno_a_prijmeni = zaznam.split(' ') - jmeno = jmeno_a_prijmeni[0] - prijmeni = jmeno_a_prijmeni[1] - vysledek.append(jmeno.capitalize() + ' ' + prijmeni.capitalize()) - return vysledek -``` -{% endfilter %} - ## Seznamy a náhoda -Modul `random` obsahuje dvě funkce, které se hodí k seznamům. -Jako `random.randrange`, obě mají něco -společného s náhodou. +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í. -Jako metoda `sort` i funkce `shuffle` nic nevrací. +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 list(range(2, 11)) + ['J', 'Q', 'K', 'A']: + for hodnota in ciselne_hodnoty + pismenne_hodnoty: balicek.append(str(hodnota) + barva) print(balicek) @@ -502,86 +492,12 @@ print(balicek) ``` A funkce `choice` ze seznamu vybere jeden náhodný prvek. -S použitím seznamu tak můžeme výrazně zjednodušit -úvodní část naší staré hry kámen/nůžky/papír: +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) ``` - -## Vnořené seznamy - -A perlička na konec! -Na začátku tohoto textu je napsáno, že seznam -může obsahovat jakýkoli typ hodnot. -Může třeba obsahovat i další seznamy: - -```python -seznam_seznamu = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] -``` - -Takový seznam se chová docela normálně – jdou -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, -můžeme 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ůžeme 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í, ptáš se? -Stejně jako vnořené cykly `for` -nám umožnily vypsat tabulku, vnořené seznamy -nám umožní si tabulku „zapamatovat”. - -```python -def vytvor_tabulku(velikost=11): - seznam_radku = [] - for a in range(velikost): - radek = [] - for b in range(velikost): - radek.append(a * b) - seznam_radku.append(radek) - return seznam_radku - -nasobilka = vytvor_tabulku() - -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/list/static/methods.svg b/lessons/beginners/list/static/methods.svg new file mode 100644 index 0000000000..2c67d7dde8 --- /dev/null +++ b/lessons/beginners/list/static/methods.svg @@ -0,0 +1,1305 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lessons/beginners/nested-list/index.md b/lessons/beginners/nested-list/index.md new file mode 100644 index 0000000000..588a457771 --- /dev/null +++ b/lessons/beginners/nested-list/index.md @@ -0,0 +1,78 @@ +# 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_tabulku(velikost=11): + seznam_radku = [] + for a in range(velikost): + radek = [] + for b in range(velikost): + radek.append(a * b) + seznam_radku.append(radek) + return seznam_radku + +nasobilka = vytvor_tabulku() + +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 new file mode 100644 index 0000000000..7167651c4a --- /dev/null +++ b/lessons/beginners/nested-list/info.yml @@ -0,0 +1,4 @@ +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/range/index.md b/lessons/beginners/range/index.md new file mode 100644 index 0000000000..53b52e3bd3 --- /dev/null +++ b/lessons/beginners/range/index.md @@ -0,0 +1,131 @@ +# 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 new file mode 100644 index 0000000000..10fbe5ccee --- /dev/null +++ b/lessons/beginners/range/info.yml @@ -0,0 +1,4 @@ +title: Range - sekvence čísel +style: md +attribution: Pro PyLadies Brno napsal Petr Viktorin, 2019. +license: cc-by-sa-40 diff --git a/lessons/beginners/tuple/index.md b/lessons/beginners/tuple/index.md index eec9cfeafc..0d34379bff 100644 --- a/lessons/beginners/tuple/index.md +++ b/lessons/beginners/tuple/index.md @@ -3,39 +3,45 @@ Když už známe seznam, podívejme se na jeho sestřičku: takzvanou n-tici (angl. *tuple*). -N-tice, podobně jako seznam, -může obsahovat n prvků. +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] -> Existují i n-tice s jedním prvkem (hmm… „jednice”?) +> Pro úplnost: existují i n-tice s jedním prvkem (hmm… „jednice”?) > a s nula prvky (prázdné n-tice, angl. *empty tuple*), -> ale těmi se ze začátku nebudeme zabývat. +> 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. +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ů. -Dají se ale použít v cyklu `for` -a dají se z nich číst jednotlivé prvky. +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) -print('První je {}'.format(osoby[0])) + +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 jmeno in 'Hynek', 'Vilém', 'Jarmila':` -> ve skutečnosti používá n-tici! +> `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é @@ -44,6 +50,10 @@ 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): @@ -54,8 +64,8 @@ print(seznam_dvojic) N-tice se hodí, pokud chceš z funkce vrátit víc než jednu hodnotu. -Prostě v příkazu `return` oddělíš vracené hodnoty čárkou. -Vypadá to, že vracíš několik hodnot, ale +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 @@ -64,69 +74,28 @@ def podil_a_zbytek(a, b): ``` > [note] -> Tahle funkce už mimochodem v Pythonu je: jmenuje se -> `divmod` a je vždy k dispozici -> (nemusí se importovat). +> Funkce „podíl a zbytek“ je mimochodem k dispozici přímo v Pythonu +> pod jménem `divmod`. -Python umí ještě jeden trik: pokud chceš přiřadit -do několika proměnných najednou, stačí je na levé +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. +dát nějakou „složenou” hodnotu – třeba právě n-tici. ```python podil, zbytek = podil_a_zbytek(12, 5) ``` -N-tice se k tomuto účelu hodí nejvíc, ale -jde to se všemi hodnotami, které jdou použít ve `for`: - -```python -x, o = 'xo' -jedna, dva, tri = [1, 2, 3] -``` - -## Funkce, které vracejí n-tice - -`zip` je zajímavá funkce. -Používá se ve `for` cyklech, podobně jako funkce `range`, která „dává” čísla. - -Když funkce `zip` dostane dva seznamy -(či jiné věci použitelné ve `for`), -„dává” dvojice, a to tak, že nejdřív spáruje -první prvek jednoho seznamu s prvním prvkem -druhého seznamu, -pak druhý s druhým, třetí s třetím a tak dál. - -Hodí se to, když máš dva seznamy se stejnou -strukturou – příslušné prvky k sobě „patří” -a chceš je zpracovávat společně: - -```python -osoby = 'máma', 'teta', 'babička', 'vrah' -vlastnosti = 'hodná', 'milá', 'laskavá', 'zákeřný' -for osoba, vlastnost in zip(osoby, vlastnosti): - print('{} je {}'.format(osoba, vlastnost)) -``` - -Když `zip` dostane tři seznamy, -bude tvořit trojice, ze čtyř seznamů nadělá čtveřice a tak dále. - -Další funkce, která vrací dvojice, je `enumerate`. -Jako argument bere seznam (či jinou věc použitelnou -ve `for`) a vždy spáruje index (pořadí v seznamu) s příslušným prvkem. -Jako první tedy dá -(0, *první prvek seznamu*), potom -(1, *druhý prvek seznamu*), -(2, *třetí prvek seznamu*) -a tak dále. - -```python -prvocisla = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] +> [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] +> ``` -for i, prvocislo in enumerate(prvocisla): - print('Prvočíslo č.{} je {}'.format(i, prvocislo)) -``` ## Malé n-tice @@ -155,25 +124,27 @@ 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 index–hodnota z `enumerate` -je n-tice. +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ě přihlášených do soutěže -může být občas prázdný. +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číme pracovat se slovníky, -zjistíme že seznamy tam nepůjdou použít jako klíče. +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. -Řiď se instinktem. :) +Často není úplně jasné, který typ použít. +V takovém případě je to pravděpodobně jedno. diff --git a/lessons/beginners/zip-enumerate/index.md b/lessons/beginners/zip-enumerate/index.md new file mode 100644 index 0000000000..cd748d2227 --- /dev/null +++ b/lessons/beginners/zip-enumerate/index.md @@ -0,0 +1,259 @@ +# 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'{cislo + 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 new file mode 100644 index 0000000000..566e2e99ed --- /dev/null +++ b/lessons/beginners/zip-enumerate/info.yml @@ -0,0 +1,4 @@ +title: Iterátory n-tic +style: md +attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2019. +license: cc-by-sa-40