Cvičení č. 16 rozšířené o poznámky ze cvičení a řešení některých příkladů (ZS 2025/26)¶

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

Iterovatelné objekty (iterables) v Pythonu jsou objekty, přes které může být provedena iterace, např. pomocí for-cyklu. Mezi iterovatelné objekty patří např. kontejnery. Za iteraci na iterovatelném objektu je zodpovědný objekt zvaný iterátor, který postupně poskytuje jednotlivé prvky iterovatelného objektu.

Iterátor pro daný iterovatelný objekt získáme pomocí vestavěné funkce iter(iterable), další prvek v iteraci získáme pomocí funkce next(iterator). Jakmile iterátor poskytne všechny prvky iterovatelného objektu, vyvolá výjimku StopIteraion, což iteraci ukončí:

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

my_iterator = iter(my_list)
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
1
5
4
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[1], line 7
      5 print(next(my_iterator))
      6 print(next(my_iterator))
----> 7 print(next(my_iterator))

StopIteration: 

V programech s iterátory často nepracujeme přímo, ale skrytě - na iterátorech je založený for-cyklus:

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

for x in my_list:
    ... # do something

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

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

iterator = iter(my_list)
while True:
    try:
        x = next(iterator)
        ... # do something        
    except StopIteration:
        break
In [1]:
# další ukázka
s = [1, 2, 4, 3]
x = reversed(s)
x
Out[1]:
<list_reverseiterator at 0x7fe2c0121a20>
In [2]:
for x in reversed(s):
    print(x, end = " ")

t = tuple(reversed(s))
t
3 4 2 1 
Out[2]:
(3, 4, 2, 1)

Seznámili jsme se s několika užitečnými vestavěnými funkcemi v Pythonu, 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.

A s dalšími vestavěnými funkcemi nad iterovatelnými objekty:

Funkce Krátký popis
sorted(iterable, /, *, key=None, reverse=False) Vrací setříděný seznam prvků z iterable.
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.

Generátory¶

Generátory jsou zvláštním druhem iterátoru, který neiteruje přes existující iterovatelný objekt, ale přes průběžně generované hodnoty. Můžeme ho vytvořit pomocí tzv. generátorové funkce (generator function) nebo pomocí tzv. generátorového výrazu (generator expression).

Generátorová funkce je taková funkce, která hodnoty nevrací (pomocí příkazu return), ale generuje (pomocí příkazu yield). Například:

In [19]:
def print_squares(n):
    for x in range(n):
        print(x ** 2, end = " ") 
print_squares(8)
0 1 4 9 16 25 36 49 
In [20]:
def list_squares(n):
    l = []
    for x in range(n):
        l.append(x ** 2)
    return l
list_squares(8)
Out[20]:
[0, 1, 4, 9, 16, 25, 36, 49]
In [2]:
def generate_squares(n):
    for x in range(n):
        yield x ** 2
In [21]:
# přes generátor mohu iterovat:
for x in generate_squares(8):
    print(x, end = " ")
0 1 4 9 16 25 36 49 
In [22]:
# nebo generátor převedu na kontejner:
tuple(generate_squares(8))
Out[22]:
(0, 1, 4, 9, 16, 25, 36, 49)

Kdykoliv v programu zavoláme generátorovou funkci, funkce vrátí nový generátor - tj. iterátor, který iteruje přes hodnoty generované pomocí yield.

In [23]:
m = generate_squares(5)
print(m)
<generator object generate_squares at 0x7f6bb0845ff0>
In [5]:
for x in m:
    print(x, end = " ")
0 1 4 9 16 
In [24]:
# ukázka: generátor se použitím vyčerpává
for x in m:
    print(x, end = " ")
print("\npodruhe")
for x in m:
    print(x, end = " ")
0 1 4 9 16 
podruhe
In [25]:
# ukázka: generátor se použitím vyčerpává
# řešení: pokaždé iteruji přes nový generátor:
for x in generate_squares(5):
    print(x, end = " ")
print("\npodruhe")
for x in generate_squares(5):
    print(x, end = " ")
0 1 4 9 16 
podruhe
0 1 4 9 16 

Podrobněji: Generátor si pamatuje aktuální pozici v rámci funkce. Při volání next() iterátor spustí generátorovou funkci od aktuální pozice. Provádění příkazů přeruší u příkazu yield a jako návratovou hodnotu funkce next() poskytne příslušnou hodnotu. Při dalším volání next() funkce pokračuje dál od daného místa. Například:

In [3]:
def generate_whatever():
    x = 1
    yield x
    x = 2
    yield x
    yield "the end"
for x in generate_whatever():
    print(x)
1
2
the end
In [4]:
m = generate_whatever()
print(m)
<generator object generate_whatever at 0x7fe2c02e4a00>
In [5]:
# opět si zkuste pustit tuto buňku opakovaně:
print(next(m))
1
In [9]:
# generátor můžeme převést na iterovatelný objekt:
s = tuple(generate_whatever())
print(s)
(1, 2, 'the end')
In [6]:
# nebo přes něj můžeme iterovat:
for x in generate_whatever():
    print(x)
1
2
the end
In [10]:
# další ukázka: generátor hodnot od 0 do n-1:
def my_range(n):
    i = 0
    while i < n:
        yield i
        i = i + 1

for x in my_range(21):
    print(x, end = " ")
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

Poznámka: Generátory šetří časové i paměťové nároky programu: nemusíme uchovávat všechna data v paměti najednou (např. v kontejneru), hodnoty jsou generované jedna po druhé až ve chvíli, kdy jsou potřeba.

Příklady¶

1a. Generátor dělitelů. Napište generátorovou funkci, která vrátí generátor všech dělitelů zadaného přirozeného čísla n. Čísla budou generována v pořadí od nejmenšího po největší.

In [19]:
def divisors_print(n): # vypíše dělitele
    for i in range(1, n+1):
        if n % i == 0:
            print(i, end = " ")
            
divisors_print(504)
1 2 3 4 6 7 8 9 12 14 18 21 24 28 36 42 56 63 72 84 126 168 252 504 
In [27]:
def divisors(n): # vrátí seznam dělitelů
    l = []
    for i in range(1, n+1):
        if n % i == 0:
            l.append(i)
    return l
print(divisors(504))
[1, 2, 3, 4, 6, 7, 8, 9, 12, 14, 18, 21, 24, 28, 36, 42, 56, 63, 72, 84, 126, 168, 252, 504]
In [7]:
def divisors_generator(n):  # vrátí generátor dělitelů
    for i in range(1, n+1):
        if n % i == 0:
            yield i
s = tuple(divisors_generator(27))
print(s)
for i in divisors_generator(27):
    print(i, end = " ")
(1, 3, 9, 27)
1 3 9 27 
In [23]:
def divisors_generator(n):
    for i in range(1, n+1):
        if n % i == 0:
            yield i

n = 24
for divisor in divisors_generator(n):
    print(divisor, end=' ')

print(list(divisors_generator(504)))

assert list(divisors_generator(504)) == [1, 2, 3, 4, 6, 7, 8, 9, 12, 14, 18, 21, 24, 28, 36, 42, 56, 63, 72, 84, 126, 168, 252, 504]
1 2 3 4 6 8 12 24 [1, 2, 3, 4, 6, 7, 8, 9, 12, 14, 18, 21, 24, 28, 36, 42, 56, 63, 72, 84, 126, 168, 252, 504]

2a. Generátor posloupnosti Napište generátorovou funkci, která vrátí generátor prvků následující posloupnosti (pro dané n): 1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 ... 1 2 ... n.

In [32]:
def sequence_generator(n):
    for i in range(1, n+1):
        for j in range(1, i+1):
            yield j

n = 5
generator = sequence_generator(n)
for num in generator:
    print(num, end=' ')

print(list(sequence_generator(8)))

assert list(sequence_generator(8)) == [1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8]
1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 [1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8]

4a. Generátor délek slov. Naprogramujte generátorovou funkci, která vrátí generátor délek všech slov v textu. Předpokládejme, že slova jsou oddělená pomocí obyčejných mezer (možná několika za sebou). Hodit se mohou i metody pro stringy (např. split()).

In [8]:
text = "Toto  je  příklad textu s několika slovy oddělenými mezerami"
print(text.split())
['Toto', 'je', 'příklad', 'textu', 's', 'několika', 'slovy', 'oddělenými', 'mezerami']
In [31]:
def word_lengths_generator(text):
    for word in text.split():
        yield len(word)
        #print(word, end = ", ")
   

text = "Toto  je  příklad textu s několika slovy oddělenými mezerami"

for length in word_lengths_generator(text):
    print(length, end=' ')

assert (list(word_lengths_generator(text)) == [4, 2, 7, 5, 1, 8, 5, 10, 8])
4 2 7 5 1 8 5 10 8 
In [ ]:
# skoncili jsme zde

Generátorové výrazy a generátorová notace modifikovatelných kontejnerů¶

Generátorové výrazy umožňují vytvářet jednoduché generátory elegantně bez nutnosti vytváření generátorových funkcí. Generátorová notace (comprehension) u modifikovatelných (mutable) kontejnerů (list, set, dict) nám pak výrazně zjednodušuje vytváření a zpracování těchto objektů.

Základní syntaxe je následující:

python:
new_generator = (expression for member in iterable)
new_list = [expression for member in iterable]
new_set  = {expression for member in iterable}
new_dict = {key:expression for member in iterable}

Výrazy se liší jen typem použitých závorek. Pokud použijeme kulaté závorky, bude výsledkem generátor, jinak příslušný kontejner. Použitou syntaxi si vysvětlíme na několika následujících příkladech:

Například pro seznam příkaz s = [expression for member in iterable] můžeme přepsat jako:

python:
s = []
for member in iterable:
    s.append(expression)

Obdobně pro množinu s = {expression for member in iterable}:

python:
s = set()
for member in iterable:
    s.add(expression)

Pro slovník d = {key:expression for member in iterable}:

python:
d = {}
for member in iterable:
    d[key] = expression

Ukážeme si to na konkrétních příkladech:

In [9]:
# ukázky pro seznam + bez generátorové notace vs. s ní
t = (1, 6, 2)

#s = [ ... for ... in ... ]
s = [ x for x in t ]
print(s)

s = [ 1 for x in t ]
print(s)

s = [ 2 * x for x in t ]
print(s)
[1, 6, 2]
[1, 1, 1]
[2, 12, 4]
In [11]:
# to samé dvěma způsoby:
s = [ 2 * x for x in t ]
print(s)

s = []
for x in t:
    s.append(2*x)
s
[2, 12, 4]
Out[11]:
[2, 12, 4]
In [12]:
# pro množinu,...
s = { 2 * x for x in t }
print(s)
{2, 12, 4}
In [13]:
# pro slovnik...
s = { x:str(x) for x in t }
print(s)
{1: '1', 6: '6', 2: '2'}
In [14]:
# přímo generátor...
g = (2 * x for x in t)
g
Out[14]:
<generator object <genexpr> at 0x7fe2c0130e10>
In [15]:
list(g)
Out[15]:
[2, 12, 4]
In [18]:
# dvakrát to samé:
s = list(2 * x for x in t)
s
Out[18]:
[2, 12, 4]
In [17]:
s = [2 * x for x in t]
s
Out[17]:
[2, 12, 4]
In [48]:
# generátorová notace seznamu, množiny a slovníku:
my_tuple = (1,8,3)

my_list = [x*2 for x in my_tuple]
my_set  = {x*2 for x in my_tuple}
my_dict = {x:x*2 for x in my_tuple}

print(my_tuple)
print(my_list)
print(my_set)
print(my_dict)
(1, 8, 3)
[2, 16, 6]
{16, 2, 6}
{1: 2, 8: 16, 3: 6}
In [23]:
# když chci tuple s prvky vynásobenými dvěma: 
# nejméně efektivní způsob: pomocí += 
my_tuple = (1, 8, 3)
new_tuple = ()
for x in my_tuple:
    new_tuple += (2*x,)
new_tuple
Out[23]:
(2, 16, 6)
In [21]:
# když chci tuple s prvky vynásobenými dvěma: 
# efektivněji: převedu na list + append
my_tuple = (1, 8, 3)
s = []
for x in my_tuple:
    s.append(2*x)
new_tuple = tuple(s)
new_tuple
Out[21]:
(2, 16, 6)
In [20]:
# když chci tuple s prvky vynásobenými dvěma: 
# nejefektivněji a na jeden řádek: použiji generátor
my_tuple = (1, 8, 3)
new_tuple = tuple(2*x for x in my_tuple)
new_tuple
Out[20]:
(2, 16, 6)
In [28]:
# generátorový výraz:
my_generator = (x*2 for x in my_tuple)
print(my_generator)

for x in my_generator:
    print(x, end = " ")

# generátorový výraz přímo ve for-cyklu:
for x in (x*2 for x in my_tuple):
    print(x, end = " ") 
<generator object <genexpr> at 0x7fd64bc48a00>
2 16 6 2 16 6 

Výraz my_generator = (x**2 for x in my_tuple) je zkratkou následujícího kódu:

In [31]:
# generátorová funkce: 
def generate_squares(source):
    for x in source:
        yield x ** 2
        
my_generator = generate_squares(my_tuple)
my_generator
Out[31]:
<generator object generate_squares at 0x7fd66151b850>
In [30]:
my_generator = (x**2 for x in my_tuple)
my_generator
Out[30]:
<generator object <genexpr> at 0x7fd661519be0>

Výraz new_list = [expression for member in iterable] můžeme ekvivalentně zapsat jako: new_list = list(expression for member in iterable). Obdobně pro množinu. Pro slovník bude syntaxe lehce odlišná - argumentem funkce dict může být generátor dvojic (klíč, hodnota):

In [23]:
my_tuple = (1,8,3)

my_list = list(x*2 for x in my_tuple)
my_set  = set(x*2 for x in my_tuple)
new_tuple = tuple(x*2 for x in my_tuple) # mohu vytvořit i tuple
my_dict = dict((x, x*2) for x in my_tuple)

print(my_list)
print(my_set)
print(new_tuple)
print(my_dict)

my_list = [x*2 for x in my_tuple]
my_set  = {x*2 for x in my_tuple}
my_dict = {x: x*2 for x in my_tuple}
print(my_list)
print(my_set)
print(my_dict)
[2, 16, 6]
{16, 2, 6}
(2, 16, 6)
{1: 2, 8: 16, 3: 6}
[2, 16, 6]
{16, 2, 6}
{1: 2, 8: 16, 3: 6}
In [33]:
my_dict = {x: x*2 for x in my_tuple}
my_dict
Out[33]:
{1: 2, 8: 16, 3: 6}
In [22]:
# funkce dict chce kontejner/iterátor dvojic (klíč, hodnota)
dict([(2,2), (4,5)])
Out[22]:
{2: 2, 4: 5}
In [34]:
my_dict = dict((x, x*2) for x in my_tuple)
my_dict
Out[34]:
{1: 2, 8: 16, 3: 6}

Generové výrazy můžeme rozšířit o podmínky a můžeme pomocí nich kombinovat hodnoty z více zdrojů:

  • Filtrování (obdoba funkce filter):
python:
    (expression for member in iterable if condition)
  • Kombinace hodnot z více zdrojů:
python:
  (expression for member1 in iterable1 (if condition1)
             for member2 in iterable2 (if condition2) 
             ...
             for memberN in iterableN (if conditionN))

Příkaz s = [f(x) for x in iterable if condition] je zkratkou za:

python:
s = []
for x in iterable:
    if condition:
        s.add(f(x))

Příkaz s = [x+y for x in range(5) for y in range(x)] je zkratkou za:

python:
s = []
for x in range(5):
    for y in range(x):
        s.append(x+y)

Opět si to ukážeme na konkrétních příkladech:

In [36]:
# generátorová notace seznamu (list comprehension):
s = [x for x in range(1, 11)]
print(s, end ="\n\n")
t = [2 ** x for x in range(1, 11)]
print(t, end ="\n\n")
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

[2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]

In [37]:
# generátorová notace množiny (set comprehension) s podmínkou:
m = {x for x in s if x % 2== 0}
print(m, end ="\n\n")
{2, 4, 6, 8, 10}

In [38]:
# generátorová notace seznamu s více zdroji:
trojice =[(i,j,k) for i in [1,2,3] for j in {True, False} for k in "AB"]
print(trojice, end ="\n\n")
[(1, False, 'A'), (1, False, 'B'), (1, True, 'A'), (1, True, 'B'), (2, False, 'A'), (2, False, 'B'), (2, True, 'A'), (2, True, 'B'), (3, False, 'A'), (3, False, 'B'), (3, True, 'A'), (3, True, 'B')]

In [39]:
# generátorová notace seznamu s využitím funkce zip:
trojice =[(i,j,k) for (i,j,k) in zip([1,2,3], {True, False},"AB")]
print(trojice, end ="\n\n")
[(1, False, 'A'), (2, True, 'B')]

In [24]:
# generátorová notace slovníku:
šachovnice ={(i,j) : "" for i in "abcdefgh" for j in range(1, 9)}
print(šachovnice)
print()
šachovnice["c",4] = "bílý pěšec"
šachovnice["a",1] = "černý král"
print(šachovnice)
{('a', 1): '', ('a', 2): '', ('a', 3): '', ('a', 4): '', ('a', 5): '', ('a', 6): '', ('a', 7): '', ('a', 8): '', ('b', 1): '', ('b', 2): '', ('b', 3): '', ('b', 4): '', ('b', 5): '', ('b', 6): '', ('b', 7): '', ('b', 8): '', ('c', 1): '', ('c', 2): '', ('c', 3): '', ('c', 4): '', ('c', 5): '', ('c', 6): '', ('c', 7): '', ('c', 8): '', ('d', 1): '', ('d', 2): '', ('d', 3): '', ('d', 4): '', ('d', 5): '', ('d', 6): '', ('d', 7): '', ('d', 8): '', ('e', 1): '', ('e', 2): '', ('e', 3): '', ('e', 4): '', ('e', 5): '', ('e', 6): '', ('e', 7): '', ('e', 8): '', ('f', 1): '', ('f', 2): '', ('f', 3): '', ('f', 4): '', ('f', 5): '', ('f', 6): '', ('f', 7): '', ('f', 8): '', ('g', 1): '', ('g', 2): '', ('g', 3): '', ('g', 4): '', ('g', 5): '', ('g', 6): '', ('g', 7): '', ('g', 8): '', ('h', 1): '', ('h', 2): '', ('h', 3): '', ('h', 4): '', ('h', 5): '', ('h', 6): '', ('h', 7): '', ('h', 8): ''}

{('a', 1): 'černý král', ('a', 2): '', ('a', 3): '', ('a', 4): '', ('a', 5): '', ('a', 6): '', ('a', 7): '', ('a', 8): '', ('b', 1): '', ('b', 2): '', ('b', 3): '', ('b', 4): '', ('b', 5): '', ('b', 6): '', ('b', 7): '', ('b', 8): '', ('c', 1): '', ('c', 2): '', ('c', 3): '', ('c', 4): 'bílý pěšec', ('c', 5): '', ('c', 6): '', ('c', 7): '', ('c', 8): '', ('d', 1): '', ('d', 2): '', ('d', 3): '', ('d', 4): '', ('d', 5): '', ('d', 6): '', ('d', 7): '', ('d', 8): '', ('e', 1): '', ('e', 2): '', ('e', 3): '', ('e', 4): '', ('e', 5): '', ('e', 6): '', ('e', 7): '', ('e', 8): '', ('f', 1): '', ('f', 2): '', ('f', 3): '', ('f', 4): '', ('f', 5): '', ('f', 6): '', ('f', 7): '', ('f', 8): '', ('g', 1): '', ('g', 2): '', ('g', 3): '', ('g', 4): '', ('g', 5): '', ('g', 6): '', ('g', 7): '', ('g', 8): '', ('h', 1): '', ('h', 2): '', ('h', 3): '', ('h', 4): '', ('h', 5): '', ('h', 6): '', ('h', 7): '', ('h', 8): ''}

Příklady¶

Předně můžete zkusit naprogramovat příklady z minulého cvičení tak, aby místo funkcí map a filter používaly generátory.

1b. Seznam dělitelů. Napište funkci, která vrátí seznam všech dělitelů zadaného přirozeného čísla seřazený od nejmenšího po největší. Úlohu řeště tentokrát pomocí generátorové notace seznamu (list comprehension). Příklad navazuje na příklad 1a výše.

In [43]:
def divisors_list(n):
     # return  [ ... for ... in ... if ... ] 
     # podívejte se na řešení pomocí generátorových funkcí, bude to "stejné, jen zpřeházené"
     return  [ i for i in range(1, n+1) if n % i == 0]

n = 24
for divisor in divisors_list(n):
    print(divisor, end=' ')
assert divisors_list(504) == [1, 2, 3, 4, 6, 7, 8, 9, 12, 14, 18, 21, 24, 28, 36, 42, 56, 63, 72, 84, 126, 168, 252, 504]
1 2 3 4 6 8 12 24 

1c. Generátor dělitelů podruhé. Šla by předchozí funkce jednoduše upravit tak, aby místo seznamu dělitelů vracela generátor dělitelů?

In [44]:
def divisors_generator(n): # liší se jen závorkami
     return  ( i for i in range(1, n+1) if n % i == 0)

n = 24
for divisor in divisors_generator(n):
    print(divisor, end=' ')
assert list(divisors_generator(504)) == [1, 2, 3, 4, 6, 7, 8, 9, 12, 14, 18, 21, 24, 28, 36, 42, 56, 63, 72, 84, 126, 168, 252, 504]
1 2 3 4 6 8 12 24 

2b. Generátor posloupnosti podruhé Napište funkci, která vrátí generátor prvků následující posloupnosti (pro dané n): 1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 ... 1 2 ... n. Tentokrát úlohu řešte pomocí generátorového výrazu. Funkce bude vracet generátor. Příklad navazuje na příklad 2a výše.

In [45]:
def sequence(n):
    # podívejte se na řešení pomocí generátorových funkcí, bude to "stejné, jen zpřeházené"
    return (j for i in range(1, n+1) for j in range(1, i+1))
    
n = 8

nums = sequence(n)
for num in nums:
    print(num, end=' ')
print(list(sequence(n)))

nums = sequence(n)
assert list(nums) == [1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8]
1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 1 2 3 4 5 6 1 2 3 4 5 6 7 1 2 3 4 5 6 7 8 [1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8]
  1. Prohoď klíče a hodnoty. Naprogramujte funkci, která na základě slovníku vytvoří nový slovník, kde budou prohozené klíče a hodnoty. Úlohu řešte pomocí generátorové generátorové notace slovníku.
In [28]:
# připomenutí:
my_tuple = (1, 8, 3)

my_dict = dict((x, x*2) for x in my_tuple)
my_dict = {x: x*2 for x in my_tuple}
my_dict
Out[28]:
{1: 2, 8: 16, 3: 6}
In [47]:
original_dict = {'a': 1, 'b': 2, 'c': 3}
for key in original_dict:
    print(key)
for key, value in original_dict.items():
    print(key, value)
a
b
c
a 1
b 2
c 3
In [25]:
def swap_keys_and_values(input_dict):
    return {input_dict[key]:key  for key in input_dict}
    
def swap_keys_and_values(input_dict):
    return {value:key  for key, value in input_dict.items()}
    
def swap_keys_and_values(input_dict):
    return dict((value, key)  for key, value in input_dict.items())

original_dict = {'a': 1, 'b': 2, 'c': 3}
swapped_dict = swap_keys_and_values(original_dict)
print(swapped_dict)
assert swapped_dict == {1: 'a', 2: 'b', 3: 'c'}
{1: 'a', 2: 'b', 3: 'c'}

4b. Délka slov. Naprogramujte funkci, která zjistí délku každého slova v textu. Předpokládejme, že slova jsou oddělená pomocí obyčejných mezer (možná několika za sebou). Úlohu řešte tentokrát pomocí generátorového výrazu nebo generátorové notace seznamu. Funkce bude vracet generátor nebo seznam. Hodit se mohou i metody pro stringy (např. split()).

In [52]:
"Toto  je  příklad textu s několika slovy oddělenými mezerami".split()
Out[52]:
['Toto',
 'je',
 'příklad',
 'textu',
 's',
 'několika',
 'slovy',
 'oddělenými',
 'mezerami']
In [29]:
# podívejte se na řešení pomocí generátorových funkcí
def word_lengths_generator(text):
    for x in text.split():
        yield len(x)
In [53]:
def word_lengths(text):
   return [ len(slovo) for slovo in text.split()]

text = "Toto  je  příklad textu s několika slovy oddělenými mezerami"

for length in word_lengths(text):
    print(length, end=' ')
print(list(word_lengths(text)))

assert (list(word_lengths(text)) == [4, 2, 7, 5, 1, 8, 5, 10, 8])
4 2 7 5 1 8 5 10 8 [4, 2, 7, 5, 1, 8, 5, 10, 8]

4c. Jen krátká slova. Naprogramujte funkci, která z textu odstraní všechna moc dlouhá slova, tj. slova, která jsou delší než n písmen. Předpokládejme, že slova jsou oddělená pomocí obyčejných mezer. Úlohu řešte s využitím generátorové notace. Hodit se mohou i metody pro stringy (např. join() a split()).

In [32]:
s = "Toto  je  příklad textu s několika slovy oddělenými mezerami".split()
t = "_".join(s)
print(s)
print(t)
['Toto', 'je', 'příklad', 'textu', 's', 'několika', 'slovy', 'oddělenými', 'mezerami']
Toto_je_příklad_textu_s_několika_slovy_oddělenými_mezerami
In [36]:
def remove_long_words(text, n):
    return " ".join([ slovo for slovo in text.split() if len(slovo) <= n ])

text = "Toto je příklad textu s několika slovy oddělenými mezerami"

new_text = remove_long_words(text, 5)
print(new_text)
assert remove_long_words(text,6) == "Toto je textu s slovy"
Toto je textu s slovy

4d. Zkrať moc dlouhá slova. Naprogramujte funkci, která z textu zkrátí na n písmen všechna moc dlouhá slova, tj. slova, která jsou delší než n písmen. Předpokládejme, že slova jsou oddělená pomocí obyčejných mezer. Úlohu řešte s využitím generátorové notace. Hodit se mohou i metody pro stringy (např. join() a split()).

In [34]:
def shorten_long_words(text, n):
    return " ".join(slovo[:n] for slovo in text.split())

text = "Toto je příklad textu s několika slovy oddělenými mezerami"

new_text = shorten_long_words(text, 3)
print(new_text)
assert shorten_long_words(text,6) == "Toto je příkla textu s několi slovy odděle mezera"
Tot je pří tex s něk slo odd mez
  1. Bláznivé křížení zvířat podruhé. Máme dva seznamy s, t s názvy zvířat. Napište funkci merge_animals(s, t), která vytvoří nový seznam, který bude obsahovat "zkřížené" názvy zvířat tak, že z prvního názvu vezmeme vždy první polovinu a z druhého druhou polovinu. Např. 'hroch' a 'tigr' -> 'hrogr'. Úlohu řešte tentokrát pomocí generátorové notace. Hodit se může i funkce zip().
In [ ]:
def merge_animals(s, t):
    ...

print(merge_animals(["hroch","potkan"],["žirafa","orangutan","lev"]))
assert merge_animals(["hroch","potkan"],["žirafa","orangutan","lev"]) == ['hrafa', 'potgutan']
  1. Frekvenční analýza písmen. Napište funkci freq_analysis(text), která spočítá výskyt jednotlivých písmen (znaků) ve vstupním textu a vrátí výsledek jako slovník s prvky písmeno: počet výskytů. Malá a velká písmena nebudeme rozlišovat. Úlohu řešte tentokrát pomocí generátorové notace slovníku. Hodit se mohou i různé metody pro stringy (např. count(), lower(), upper() nebo isalpha()). Písmena a počty jejich výskytů vypište setříděné sestupně podle počtu výskytů.
In [ ]:
def freq_analysis(text):
    ...

d = freq_analysis("All the world is a stage and all the men and women merely players.")

print(d)
print(sorted(d.items()))

# vypište setříděné sestupně podle počtu výskytů:
...
None
  1. Frekvenční analýza slov. Napište funkci freq_analysis_words(text), která spočítá výskytů jednotlivých slov ve vstupním textu a vrátí výsledek jako slovník s prvky slovo: počet výskytů. Malá a velká písmena nebudeme rozlišovat. Úlohu řešte tentokrát pomocí generátorové notace slovníku. Hodit se mohou i různé metody pro stringy (např. count(), lower(), upper() nebo split()). Slova a počty výskytů vypište setříděné vzestupně podle abecedy.
In [ ]:
def freq_analysis(text):
    ...

d = freq_analysis("This is a test. This is only a test, not a real situation.")

print(d)

# vypište setříděné vzestupně podle abecedy:
...
  1. indexy Napište funkci, která vrátí seznam všech indexů, kde se v řetězci string vyskytuje znak char. Úlohu řešte tentokrát pomocí generátorové notace seznamu nebo generátorové funkce. Hodit se může i funkce enumerate().
In [ ]:
# generátorová notace:
def find_letter_indexes(string, letter):
    ...

print(find_letter_indexes("hello world", "l"))
assert find_letter_indexes("hello world", "l") == [2, 3, 9]
In [ ]:
# generátorová funkce:
def generate_letter_indexes(string, letter):
    ...

print(list(generate_letter_indexes("hello world", "l")))
assert list(generate_letter_indexes("hello world", "l")) == [2, 3, 9]

8b. indexy Napište funkci, která vrátí seznam všech indexů, na kterých v řetězci string začíná podstring substring. Úlohu řešte tentokrát pomocí generátorové notace seznamu nebo generátorové funkce. Hodit se může i funkce enumerate().

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

print(find_substring_indexes("ara aaraara arara", "ara"))
assert find_substring_indexes("ara aaraara arara", "ara") == [0, 5, 8, 12, 14]
In [ ]:
# generátorová funkce:
def generate_substring_indexes(string, substring):
    ...

print(list(generate_letter_indexes("ara aaraara arara", "ara")))
assert list(generate_letter_indexes("ara aaraara arara", "ara")) == [0, 5, 8, 12, 14]