V minulém díle jste viděli…¶

Předtavili jsme si polnohodnotné vývojové prostředí VSCode, resp. VSCodium:

  • instalace VSCodia a potřebných rozšíření, základní nastavení prostředí
  • vyvoření zdrojového souboru v Pythonu (koncovka .py) a jeho spuštění ve VSCodiu i mimo něj
  • použití debuggeru z rozšíření Python pro VSCodium
  • zobrazení a editace Jupyter notebooků ve VSCodiu

Ukázali jsme si práci s modulem turtle pro kreslení obrázků pomocí želví grafiky, například:

import turtle

# draw a square
def square():
    for i in range(4):
        turtle.forward(100)
        turtle.right(90)

square()

# wait until the user closes the window
turtle.mainloop()

Iterátory¶

Iterátory jsou velmi důležitým konceptem Pythonu. Umožňují nám zpracovávat data postupně nebo kombinovat hodnoty z různých zdrojů. Na předchozích cvičeních jsme již s nimi pracovali, i když skrytě, při práci s kontejnery a kdykoliv jsme v programech použili for-cyklus. Iterátory oceníme především při zpracování velkých objemů dat, kdy umožňují přístup k jednotlivým hodnotám až ve chvíli, kdy jsou potřeba, bez nutnosti ukládání celého objemu dat do paměti.

Iterovatelné objekty (iterables)¶

Základním stavebním kamenem iterace a zpracování dat v Pythonu jsou iterovatelné objekty (iterables). Iterovatelný objekt je jakýkoliv objekt, přes který může být prováděna iterace (tj. můžeme přistupovat k jednotlivým prvkům v objektu postupně, jeden po druhém, například pomocí for-cyklu). Mezi iterovatelné objekty patří řada vestavěných datových typů v Pythonu, například kontejnery (list, tuple, str, dictionary, set, ...), range nebo datové proudy (např. objekty představující soubory).

Poznámka: O datových proudech si řekneme více na některém z dalších cvičení.

In [2]:
s = [1,2,3]
for x in s:
    print(x, end = " ")
1 2 3 

Za iteraci na iterovatelném objektu, například v rámci for-cyklu, je zodpovědný objekt zvaný iterátor. Ten během iterace postupně (jeden po druhém) poskytuje další prvky iterovatelného objektu. Pamatuje si svůj vnitřní stav (ví, které prvky již poskytl a který prvek bude na řadě příště). Jakmile iterátor poskytne (právě jednou) všechny prvky iterovatelného objektu, vyvolá výjimku StopIteraion, což iteraci ukončí.

Iterátor pro daný iterovatelný objekt získáme pomocí vestavěné funkce iter(iterable):

In [3]:
my_list = [1, 5, 4]

my_iterator = iter(my_list)
print(my_iterator)
<list_iterator object at 0x7efcd76ebe50>

Další prvek v iteraci získáme pomocí vestavěné funkce next(iterator):

In [4]:
# iterátor poskytuje prvky seznamu jeden po druhém
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
1
5
4
In [5]:
# další volání 'next' vyvolá výjimku `StopIteration`
print(next(my_iterator))
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[5], line 2
      1 # další volání 'next' vyvolá výjimku `StopIteration`
----> 2 print(next(my_iterator))

StopIteration: 

Poznámka: Pro každý iterovatelný objekt může být iterátor implementován různými způsoby. Pořadí, v jakém iterátor vrací prvky, proto vždy záleží na daném iterovatelném objektu. Např. pro posloupnosti iterátor vrací prvky ve vzestupném pořadí dle jejich indexu.

Iterátor v Pythonu nelze použít opakovaně a neumí se vracet k předchozím hodnotám. Říkáme o něm, že se při použití "vyčerpává". Pokud po vyčerpání iterátoru budeme chtít provést iteraci znovu, musíme vytvořit nový iterátor. Vyzkoušejte si to na následujícím příkladu:

In [6]:
my_list = [1, 5, 4, 7, 9]
In [7]:
# vytvořte nový iterátor:
my_iterator = iter(my_list)
In [12]:
# zkuste si následující buňku pouštět opakovaně:
print(next(my_iterator))

# po vyčerpání iterátoru si vytvořte nový spuštěním předchozí buňky
9

Pro jeden iterovatelný objekt můžeme zároveň používat více iterátorů:

In [7]:
my_list = [1, 5, 4]

it_1 = iter(my_list)
it_2 = iter(my_list)
print("první: ", next(it_1))
print("první: ", next(it_1))
print(" druhý: ", next(it_2))
print("první: ", next(it_1))
print(" druhý: ", next(it_2))
první:  1
první:  5
 druhý:  1
první:  4
 druhý:  5

Poznámka: Iterovatelný objekt si můžeme představit jako knížku, která se skládá z jednotlivých stránek a iterátory si můžeme představit jako záložky v této knížce.

V programech s iterátory obvykle nepracujeme přímo, ale skrytě. Již jsme zmínili, že na iterátorech je založený for-cyklus. Při for-cyklu se pro iterovatelný objekt nejprve vytvoří iterátor a pak se na něm v cyklu volá next(), dokud se iterátor nevyčerpá. Tento princip ilustruje následující ukázka:

In [14]:
my_list = [1, 8, 3]

for x in my_list:
    print(x, end = " ")  
    ... # do something
1 8 3 

Kód v předchozí buňce můžeme přepsat tímto způsobem:

In [15]:
my_list = [1, 8, 3]

iterator = iter(my_list)
while True:
    try:
        x = next(iterator)
        print(x, end = " ")  
        ... # do something        
    except StopIteration:
        break
1 8 3 

Poznámka: S dvojicí bloků try: a except: jsme se již setkali ve veřejných testech domácích úkolů. Slouží k ošetřování (neboli odchytávání) chyb (výjimek). Pokud uvnitř bloku try: dojde k chybě, tj. k vyvolání výjimky, provádění bloku try: se okamžitě ukončí a přejde se do bloku except:. Pokud k chybě nedojde, blok except: se přeskočí. Více o výjimkách a obecně o ošetřování chyb si řekneme na jednom z následujících cvičení.

Jako zdroj hodnot pro for-cyklus můžeme použít přímo iterátor, ale je třeba si uvědomit, že se po jednom použití vyčerpá a už nelze použít znova:

In [10]:
my_list = [1, 8, 3]
iterator = iter(my_list)

print("poprvé:")
for x in iterator:
    print(x, end = " ")
print()

print("podruhé:")
for x in iterator:
    print(x, end = " ")
print()
poprvé:
1 8 3 
podruhé:

Pokud jako zdroj hodnot pro for-cyklus použijeme přímo iterovatelný objekt, předchozí problém nenastane, protože při spuštění for-cyklu se pro něj vytvoří vždy nový iterátor:

In [11]:
print("poprvé:")
for x in my_list:
    print(x, end = " ")
print()

print("podruhé:")
for x in my_list:
    print(x, end = " ")
print()
poprvé:
1 8 3 
podruhé:
1 8 3 

Poznámka: Iterátory v Pythonu pracují na zásadě líného vyhodnocování (lazy evaluation). To znamená, že prvky nejsou načítány do paměti najednou, ale jsou získávány až ve chvíli, kdy jsou potřeba. To je užitečné pro zpracování velkých datových sad, protože tak snížíme paměťové nároky programu.

Vestavěné funkce, které vrací iterátory¶

Kromě funkce iter() jsou v Pythonu i další vestavěné funkce, které vrací iterátory:

Funkce Krátký popis
reversed(sequence) Vrací iterátor, který poskytuje prvky posloupnosti sequence v opačném pořadí.
map(function, iterable) Vrací iterátor přes function(x) pro x z iterable.
filter(function, iterable) Vrací iterátor přes ta x z iterable, pro která function(x)==True.
zip(*iterables) Vrací iterátor přes n-tice (x, y,...) pro x z iterable1, y z iterable2,...
enumerate(iterable,start=0) Vrací objekt enumerate - iterátor přes dvojice (index, x) pro x z iterable.

Použití těchto funkcí si ukážeme na několika ilustračních příkladech:

In [12]:
my_list = [1,7,2,6,3,1,2]

# iterace přes prvky seznamu v opačném pořadí:
for x in reversed(my_list):
    print(x, end=" ")
print("")

# funkce 'reversed' vrací iterátor (ne seznam):
m = reversed(my_list)
print(m) 

# iterátor mohu převést na seznam:
reversed_list = list(m)
print(reversed_list)
print(my_list)
2 1 3 6 2 7 1 
<list_reverseiterator object at 0x7f0420031780>
[2, 1, 3, 6, 2, 7, 1]
[1, 7, 2, 6, 3, 1, 2]

Iterátory můžeme převézt na různé typy kontejnerů - voláním příslušné funkce (konstruktoru): např. list(iterator), tuple(iterator), set(iterator):

In [13]:
my_tuple = (1,7,2,6,3,1,2)

reversed_tuple = tuple(reversed(my_tuple))
reversed_list  = list(reversed(my_tuple))
my_set         = set(iter(my_tuple)) 

print(reversed_tuple)
print(reversed_list)
print(my_tuple)
print(my_set)
(2, 1, 3, 6, 2, 7, 1)
[2, 1, 3, 6, 2, 7, 1]
(1, 7, 2, 6, 3, 1, 2)
{1, 2, 3, 6, 7}

Funkce filter(f, iterable) vytváří iterátor, který poskytuje pouze ty prvky z iterable, pro které funkce f(x) vrací hodnotu True.

In [76]:
def is_even(number):
    return number % 2 == 0

numbers = (1, 2, 3, 4, 5, 6, 7, 8)

print("\ncyklus:", end = " ")
# iterace jen přes sudá čísla z n-tice klasicky:
for x in numbers:
    if is_even(x):
        print(x, end=" ")

# iterace jen přes sudá čísla z n-tice klasicky:
s = []
for x in numbers:
    if is_even(x):
        s.append(x)
even_numbers = tuple(s)
print("\n",even_numbers)

print("\nfilter:", end = " ")
# iterace jen přes sudá čísla z n-tice pomocí filter:
for x in filter(is_even, numbers):
    print(x, end=" ")

# iterátor mohu převést na iterovatelný objekt (např. n-tici):
even_numbers = tuple(filter(is_even, numbers))

print("\n",even_numbers)
cyklus: 2 4 6 8 
 (2, 4, 6, 8)

filter: 2 4 6 8 
 (2, 4, 6, 8)

Funkce map(f, iterable) vytváří iterátor, který poskytuje f(x) pro každý prvek x z iterable.

In [71]:
def square(number):
    return number ** 2

numbers = [1, 2, 3, 4, 5, 6, 7, 8]

print("\ncyklus:", end = " ")
# iterace přes druhé mocniny čísel ze seznamu klasicky:
for x in numbers:
    print(square(x), end=" ")

# iterace přes druhé mocniny čísel ze seznamu klasicky:
s = []
for x in numbers:
    s.append(square(x))
print("\n",s)

print("\nmap:   ", end = " ")
# iterace přes druhé mocniny čísel ze seznamu pomocí map:
for x in map(square, numbers):
    print(x, end=" ")
print("")

# iterátor mohu převést na iterovatelný objekt (např. seznam):
squares  = list(map(square, numbers))

print(squares)
cyklus: 1 4 9 16 25 36 49 64 
 [1, 4, 9, 16, 25, 36, 49, 64]

map:    1 4 9 16 25 36 49 64 
[1, 4, 9, 16, 25, 36, 49, 64]

Poznámka: Funkce filter() a map() se dají použít elegantněji v kombinaci s tzv. anonymními (lambda) funkcemi, jak ukazují následující příklady.

even_numbers = tuple(filter(lambda x: x % 2 == 0, numbers))
squares = list(map(lambda x: x ** 2, numbers))
Tato látka je ovšem nad rámec tohoto základního kurzu. Více o anonymních funkcích se dozvíte např. v dokumentaci.

Funkce zip(*iterables) vytvoří iterátor n-tic pro prvky z několika iterovatelných objektů:

In [79]:
names = ["Anna", "Bob", "Cyril"]
salaries = [25000, 35000, 45000]

print(list(zip(names, salaries)))

for name, salary in zip(names, salaries):
  print(f"{name} má plat {salary} Kč.")
[('Anna', 25000), ('Bob', 35000), ('Cyril', 45000)]
Anna má plat 25000 Kč.
Bob má plat 35000 Kč.
Cyril má plat 45000 Kč.

Pokud mají iterovatelné objekty různé počty prvků, funkce zip prvky navíc ignoruje:

In [17]:
x = [1, 2, 3, 4, 5]
y = ("a", "b", "c")
z = { 11 : "A", 12 : "B", 13 : "C", 14 : "D"}

w = list(zip(x,y,z.items()))
print(w)
[(1, 'a', (11, 'A')), (2, 'b', (12, 'B')), (3, 'c', (13, 'C'))]

Funkce enumerate(iterable, start=0) vrací enumerate objekt, což je iterátor, který poskytuje dvojice (index,x) pro každý prvek x z iterable.

In [18]:
numbers = {10,1,2,0}

print(enumerate(numbers))

# iterace:
for index, value in enumerate(numbers):
    print(f"Index: {index}, Value: {value}")

string = "abrakadabra" 
print(list(enumerate(string)))

# volitelný parametr start nastaví začátek číslování:
print(list(enumerate(string, start=10)))
<enumerate object at 0x7f04035f0ef0>
Index: 0, Value: 0
Index: 1, Value: 1
Index: 2, Value: 10
Index: 3, Value: 2
[(0, 'a'), (1, 'b'), (2, 'r'), (3, 'a'), (4, 'k'), (5, 'a'), (6, 'd'), (7, 'a'), (8, 'b'), (9, 'r'), (10, 'a')]
[(10, 'a'), (11, 'b'), (12, 'r'), (13, 'a'), (14, 'k'), (15, 'a'), (16, 'd'), (17, 'a'), (18, 'b'), (19, 'r'), (20, 'a')]

Třídění iterovatelných objektů¶

K třídění (řazení) prvků iterovatelných objektů slouží funkce sorted(iterable, /, *, key=None, reverse=False). Funkce vrací nový seznam. Ten obsahuje všechny prvky iterovatelného objektu iterable seřazené od nejmenšího po největší (nebo od největšího po nejmenší) pomocí operátorů pro porovnání (<=). Defaultně řadí funkce prvky vzestupně, pro sestupné pořadí je třeba nastavit hodnotu volitelného parametru reverse=True:

In [19]:
my_tuple = (5, 2, 3, 1, 4)

sorted_list = sorted(my_tuple)
print(sorted_list)

sorted_list_1 = sorted(my_tuple, reverse=True)
print(sorted_list_1)
[1, 2, 3, 4, 5]
[5, 4, 3, 2, 1]
In [16]:
my_tuple = (True, False, True)

sorted_list = sorted(my_tuple)
print(sorted_list)

sorted_list_1 = sorted(my_tuple, reverse=True)
print(sorted_list_1)
[False, True, True]
[True, True, False]
In [20]:
# stringy:
my_set = {"abc","abd", "ABC", "aBc", "ab", "abČ"}

sorted_list = sorted(my_set)
print(sorted_list)
['ABC', 'aBc', 'ab', 'abc', 'abd', 'abČ']
In [21]:
# porovnávat a třídit můžeme i posloupnosti:
print((0,True,"abc") > (0,False,"ab"),
      (0,True,"abc") > (0,True,"ab"))

my_list = [
    (1, True, "abc"),
    (0, True, "ab"),
    (0, False, "abc"),
    (0, True, "abc"),
    (1, True, "ab"),
    (0, False, "abc")
]
sorted_list = sorted(my_list)
print(sorted_list)
True True
[(0, False, 'abc'), (0, False, 'abc'), (0, True, 'ab'), (0, True, 'abc'), (1, True, 'ab'), (1, True, 'abc')]
In [17]:
my_dict = {3:"c", 2:"b", 4:"a"}
sorted_list = sorted(my_dict)
print(sorted_list)

sorted_list = sorted(my_dict.values())
print(sorted_list)

sorted_list = sorted(my_dict.items())
print(sorted_list)

dict_1 = dict(sorted(my_dict.items()))
print(dict_1)
[2, 3, 4]
['a', 'b', 'c']
[(2, 'b'), (3, 'c'), (4, 'a')]
{2: 'b', 3: 'c', 4: 'a'}

Funkce sorted má další volitelný parametr key, který definuje metodu nebo funkci, která bude zavolaná na každý prvek předtím, než se provede třídění.

In [80]:
my_string = "Anna and Bob decided to buy a beautiful house in Brighton"
my_list = my_string.split()

print("original:   ", my_list)

print("without key:", sorted(my_list))

print("with key:   ",sorted(my_list, key=str.lower))
original:    ['Anna', 'and', 'Bob', 'decided', 'to', 'buy', 'a', 'beautiful', 'house', 'in', 'Brighton']
without key: ['Anna', 'Bob', 'Brighton', 'a', 'and', 'beautiful', 'buy', 'decided', 'house', 'in', 'to']
with key:    ['a', 'and', 'Anna', 'beautiful', 'Bob', 'Brighton', 'buy', 'decided', 'house', 'in', 'to']
In [24]:
print("sort by lenghts:", sorted(my_list, key=len))
sort by lenghts: ['a', 'to', 'in', 'and', 'Bob', 'buy', 'Anna', 'house', 'decided', 'Brighton', 'beautiful']

Iterovatelné objekty často potřebujeme třídit podle hodnot na nějakém indexu:

In [53]:
my_tuples = [(3, 'bc', True), (3, 'ba', False), (4, 'bba', True)]

sorted_list = sorted(my_tuples)
print("default:     ", sorted_list)

def second(x):
    return x[1]
def third(x):
    return x[2]

# setřídění podle druhé hodnoty v n-tici:
sorted_list = sorted(my_tuples, key=second)
print("dle druhého: ", sorted_list)

# setřídění podle druhé hodnoty v n-tici:
sorted_list = sorted(my_tuples, key=lambda x : x[1])
print("dle druhého: ", sorted_list)

# setřídění podle třetí hodnoty v n-tici:
sorted_list = sorted(my_tuples, key=third)
print("dle třetího: ", sorted_list)
default:      [(3, 'ba', False), (3, 'bc', True), (4, 'bba', True)]
dle druhého:  [(3, 'ba', False), (4, 'bba', True), (3, 'bc', True)]
dle druhého:  [(3, 'ba', False), (4, 'bba', True), (3, 'bc', True)]
dle třetího:  [(3, 'ba', False), (3, 'bc', True), (4, 'bba', True)]

Třídění seznamů¶

Narozdíl od jiných iterovatelných objektů můžeme u seznamů kromě funkce sorted() použít také metodu sort(*, key=None, reverse=False). Ta narozdíl od funkce sorted() nevrací nový seznam, ale modifikuje ten původní:

In [26]:
my_list = [-5, 2, -3, 1, 4]

my_list.sort()
print(my_list)

my_list.sort(reverse=True)
print(my_list)

my_list.sort(reverse=True, key=abs)
print(my_list)
[-5, -3, 1, 2, 4]
[4, 2, 1, -3, -5]
[-5, 4, -3, 2, 1]

U seznamů je také třeba dávat pozor, abychom si nespletli funkci reversed(), která vrací iterátor a metodu reverse(), která modifikuje původní seznam:

In [27]:
my_list = [5, 2, 3, 1, 4]

print(list(reversed(my_list)))
print(my_list)

my_list.reverse()
print(my_list)
[4, 1, 3, 2, 5]
[5, 2, 3, 1, 4]
[4, 1, 3, 2, 5]

Další užitečné vestevěné funkce¶

Kromě funkcí, které vrací iterátory a funkce sorted existuje i řada dalších užitečných funkcí nad iterovatelnými objekty: Funkce | Krátký popis -----------------------------------|----------------------------- all(iterable) | Vrací True, pokud každý prvek z iterable má hodnotu True, jinak vrací False. any(iterable) | Vrací True, pokud alespoň jeden prvek z iterable má hodnotu True, jinak vrací False. sum(iterable,/,start=0) | Vrací součet prvků z iterable. max(iterable,/,key=None) | Vrací největší prvek z iterable. min(iterable,/,key=None) | Vrací nejmenší prvek z iterable. range(stop), range(start,stop), range(start,stop,step) | Vrací iterovatelný objekt typu range, nemodifikovatelnou posloupnost čísel.

Použití těchto funkcí si ukážeme na několika ilustračních příkladech:

In [28]:
# any, all, sum, max, min:
numbers = {0,1,2,10}
print(any(numbers), all(numbers), sum(numbers), max(numbers), min(numbers))

# range:
print(range(3))
print(list(range(3)))
True False 13 10 0
range(0, 3)
[0, 1, 2]

Poznámka: Další funkce pro práci s iterátory a iterovatelnými objekty naleznete v modulu itertools. Jsou zde například funkce vracející kombinatorické iterátory:

In [54]:
import itertools

# permutace:
print(list(itertools.permutations("abcd")))

for x in itertools.permutations("abcd"):
    print("".join(x), end=" ")
print("")

# variace:
for x in itertools.permutations("abcd",2):
    print("".join(x), end=" ")
print("")

# kombinace:
for x in itertools.combinations("abcd",2):
    print("".join(x), end=" ")
print("")

# kombinace s opakováním:
for x in itertools.combinations_with_replacement("abcd",2):
    print("".join(x), end=" ")
[('a', 'b', 'c', 'd'), ('a', 'b', 'd', 'c'), ('a', 'c', 'b', 'd'), ('a', 'c', 'd', 'b'), ('a', 'd', 'b', 'c'), ('a', 'd', 'c', 'b'), ('b', 'a', 'c', 'd'), ('b', 'a', 'd', 'c'), ('b', 'c', 'a', 'd'), ('b', 'c', 'd', 'a'), ('b', 'd', 'a', 'c'), ('b', 'd', 'c', 'a'), ('c', 'a', 'b', 'd'), ('c', 'a', 'd', 'b'), ('c', 'b', 'a', 'd'), ('c', 'b', 'd', 'a'), ('c', 'd', 'a', 'b'), ('c', 'd', 'b', 'a'), ('d', 'a', 'b', 'c'), ('d', 'a', 'c', 'b'), ('d', 'b', 'a', 'c'), ('d', 'b', 'c', 'a'), ('d', 'c', 'a', 'b'), ('d', 'c', 'b', 'a')]
abcd abdc acbd acdb adbc adcb bacd badc bcad bcda bdac bdca cabd cadb cbad cbda cdab cdba dabc dacb dbac dbca dcab dcba 
ab ac ad ba bc bd ca cb cd da db dc 
ab ac ad bc bd cd 
aa ab ac ad bb bc bd cc cd dd 

Příklady:¶

1a. palindrom Napište funkci palindrome(sequence), která ověří, zda je daná posloupnost palindromem (tj. zda se čte stejně od začátku jako od konce). Využijte některou z funkcí, se kterými jsme se seznámili na tomto cvičení (např. reversed).

In [19]:
def palindrome(sequence):
    #return sequence == sequence[::-1]
    return list(reversed(sequence)) == list(sequence)

assert palindrome("kobylamamalybok")
assert palindrome((1,23,6,6,23,1))
assert palindrome([1,23,6,6,23]) == False

1b. počet palindromů Napište funkci count_palindromes(sequence), která vrátí počet prvků z posloupnosti sequence, které jsou palindromy. Využijte některé z funkcí, se kterými jsme se seznámili na tomto cvičení (např. map,sum).

In [58]:
# nápověda 1:
print(sum([1,2,3,4]))
print(True + True)
print(sum([True, False, True]))
10
2
2
In [61]:
# nápověda 2:
word_list = ["ananas", "otto", "radar", "hello", "level", "civic", "python"]
for x in map(palindrome,word_list):
    print(x, end = " ")
print()

print(list(map(palindrome,word_list)))
print(list(filter(palindrome,word_list)))
False True True False True True False 
[False, True, True, False, True, True, False]
['otto', 'radar', 'level', 'civic']
In [24]:
# řešení bez map, sum:
def count_palindromes1(sequence):
    suma = 0
    for x in sequence:
        if palindrome(x):
            suma += 1
    return suma
    
def count_palindromes(sequence):
    suma = 0
    for x in sequence:
       suma += palindrome(x)
    return suma

word_list = ["ananas", "otto", "radar", "hello", "level", "civic", "python"]
assert count_palindromes(word_list) == 4
In [27]:
# řešení s map:
def count_palindromes(sequence):
    suma = 0
    for x in map(palindrome,word_list):
        suma += x
    return suma
word_list = ["ananas", "otto", "radar", "hello", "level", "civic", "python"]
assert count_palindromes(word_list) == 4
In [28]:
# řešení s map, sum:
def count_palindromes(sequence):
    return sum(map(palindrome,word_list))

word_list = ["ananas", "otto", "radar", "hello", "level", "civic", "python"]
assert count_palindromes(word_list) == 4

1c. palindromy Napište funkci palindromes(sequence), která vrátí seznam prvků z posloupnosti sequence, které jsou palindromy. Využijte některou z funkcí, se kterými jsme se seznámili na tomto cvičení (např. filter).

In [32]:
# řešení bez filter:
def palindromes(sequence):
    result = []
    for x in sequence:
        if palindrome(x):
            result.append(x)
    return result

word_list = ["ananas", "otto", "radar", "hello", "level", "civic", "python"]
assert palindromes(word_list) == ['otto', 'radar', 'level', 'civic']
In [34]:
# zbytečně dlouhé řešení s filter:
def palindromes(sequence):
    result = []
    for x in filter(palindrome,sequence):
        result.append(x)
    return result

word_list = ["ananas", "otto", "radar", "hello", "level", "civic", "python"]
assert palindromes(word_list) == ['otto', 'radar', 'level', 'civic']
In [35]:
# řešení s filter:
def palindromes(sequence):
    return list( filter(palindrome,sequence))

word_list = ["ananas", "otto", "radar", "hello", "level", "civic", "python"]
assert palindromes(word_list) == ['otto', 'radar', 'level', 'civic']
  1. vytvoř loginy Máme dva seznamy, v jednom jsou jména osob a v druhém jim přiřazená čísla (ID). Napište funkci, která vrátí seznam loginů. Každý login bude vytvořen tak, že vezmeme první 4 písmena ze jména, změníme je na malá, doplníme případně znakem '_' na 4 znaky , a na konec připojíme celé ID. Využijte některou z funkcí, se kterými jsme se seznámili na tomto cvičení (např. zip nebo map).
In [39]:
def f(tuple):
    name, id = tuple
    return name[:4].lower().ljust(4,'_') + str(id)

def create_logins(names, ids):
    return list(map(f,zip(names,ids)))

names = ["John", "Alice", "Eduard", "Bob", "Ed"]
ids = [143,787,546,165,798]
assert create_logins(names,ids) == ['john143', 'alic787', 'edua546', 'bob_165', 'ed__798']
  1. setřiď podle id Napište funkci, která pro seznam loginů ve tvaru "jméno"+"id", kde id je tvořeno čtyřmi číslicemi, vrátí nový seznam, kde budou loginy setříděny sestupně podle id
In [40]:
def f(x):
    return int(x[-4:])
    
def sort_logins_by_id(logins):
    return sorted(logins, key=f, reverse=True)

login_list = ["john1234", "alice5678", "bob2345", "eve8765"]
assert sort_logins_by_id(login_list) == ['eve8765', 'alice5678', 'bob2345', 'john1234']
assert login_list == ["john1234", "alice5678", "bob2345", "eve8765"]

4a. setřiď podle vzdálenosti Napište funkci, která vezme seznam bodů v n-rozměrném Euklidovském prostoru a vrátí ho setříděný vzestupně podle vzdálenosti bodů od bodu (0,0,...,0). Body jsou reprezentované jako n-tice čísel.

In [62]:
def dist(vector):
    s = 0
    for x in vector:
        s += x**2
    return s**0.5
    
def sort_vectors_by_distance(vectors):
   return sorted(vectors,key=dist)
    
vectors = [(3, 4.5, 0), (1, 2, -3), (0, 0, 0), (-1, -1, 1), (0.5, 5, -1)]
sorted_vectors = sort_vectors_by_distance(vectors)
assert sorted_vectors == [(0, 0, 0), (-1, -1, 1), (1, 2, -3), (0.5, 5, -1), (3, 4.5, 0)]
assert vectors == [(3, 4.5, 0), (1, 2, -3), (0, 0, 0), (-1, -1, 1), (0.5, 5, -1)]

4b. Změňte předchozí funkci tak, aby setřídila přímo původní seznam vectors:

In [63]:
def sort_vectors_by_distance(vectors):
    vectors.sort(key=dist)
    
vectors = [(3, 4.5, 0), (1, 2, -3), (0, 0, 0), (-1, -1, 1), (0.5, 5, -1)]
sort_vectors_by_distance(vectors)
assert vectors == [(0, 0, 0), (-1, -1, 1), (1, 2, -3), (0.5, 5, -1), (3, 4.5, 0)]
  1. známky Seznam studentů je reprezentovaný jako seznam slovníků, kde pro každého studenta je jeho jméno a seznam získaných známek. Například:
In [66]:
grades = [{'name': 'Adam', 'grades': [1, 2, 3, 1, 2]},
 {'name': 'Martin', 'grades': [1, 2, 2]},
 {'name': 'Filip', 'grades':  [1, 3]},
 {'name': 'David', 'grades':  [1, 2, 2, 3]},
 {'name': 'Jakub', 'grades':  [1, 2, 2, 1, 1]}]

5a. Napište funkci, která vypíše jména studentů a jejich průměr známek v pořadí od studenta s nejlepším průměrem známek po studenta s nejhorším průměrem. Využijte některou z funkcí, se kterými jsme se seznámili na tomto cvičení (např. sorted).

In [68]:
def average(student):
    return sum(student["grades"])/len(student["grades"]) 

def sort_students_by_average_grade(students):
    for x in  sorted(students,key=average):
      print("student:",x["name"], ", prumer znamek: ", average(x))
   
sort_students_by_average_grade(grades)
student: Jakub , prumer znamek:  1.4
student: Martin , prumer znamek:  1.6666666666666667
student: Adam , prumer znamek:  1.8
student: Filip , prumer znamek:  2.0
student: David , prumer znamek:  2.0

5b. Napište funkci, která vrátí seznam jmen studentů, kteří nemají dostatek známek (alespoň n). Využijte některou z funkcí, se kterými jsme se seznámili na tomto cvičení (např. filter).

In [ ]:
def students_with_not_enough_grades(students, n):
    ...

assert students_with_not_enough_grades(grades,4) == ['Martin', 'Filip']
['Martin', 'Filip']

6a. indexy Napište funkci, která vrátí seznam všech indexů, kde se v řetězci string vyskytuje znak char. Využijte některou z funkcí, se kterými jsme se seznámili na tomto cvičení (např. enumerate).

In [ ]:
def find_letter_indexes(string, letter):
    ...

assert find_letter_indexes("hello world", "l") == [2, 3, 9]

6b. indexy Napište funkci, která vrátí seznam všech indexů, na kterých v řetězci string začíná podstring substring.

In [ ]:
def find_substring_indexes(string, substring):
   ...

assert find_substring_indexes("ara aaraara arara", "ara") == [0, 5, 8, 12, 14]
  1. hra uhodni heslo Napište funkci guess_password, která se pokusí 'uhodnout' heslo passwd. Heslo má má danou délku n a může se skládat ze znaků uvedených v stringu chars. Funkce bude volat funkci evaluate_password, která odpoví, zda je daný string správné heslo nebo ne. Využijte některou z funkcí z modulu itertools. Vyzkoušejte si, jak dlouho bude programu trvat uhodnutí různě dlouhých a různě složitých hesel.
In [ ]:
chars = "0123456789"
#chars = "0123456789abcdefghijklmnopqrstuvwxyz"
n = 4

def evaluate_password(string):
    ''' Returns True if string == passwd
    '''
    passwd = chars[-1]*n
    return string == passwd
        
def guess_password(chars, n):
    ...

passwd = guess_password(chars, n)
print("Finished: ", passwd)
Finished:  None