Cvičení č. 15 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…¶

Minule jsme představili celou řadu metod pro práci se stringy. Tyto metody slouží k různým účelům:

  • spojování a rozdělování (např. join, split, splitlines, partition, )
  • zjišťovací metody (např. islower, isalpha, isspace)
  • vyhledávání (např. count, find, index, startswith)
  • nahrazování (např. replace, expandtabs)
  • ořezávání (např. strip, rstrip, lstrip)
  • formátování (např. lower, upper, capitalize, title)

Detaily o všech metodách třídy str nalzenete v dokumentaci https://docs.python.org/3/library/stdtypes.html#string-methods.

Ukázka:

In [1]:
print(" ".join(["This", "is", "a", "sentence."]))                  # spojení stringů
print("This is a sentence.\n This is another sentence.".split())   # rozdělení stringů

print("17.5".isnumeric(), "ahoj lidi".islower())                   # zjišťovací metody

print("To není to, co to není, je to toto!".count("to"))           # počet výskytů podstringu
print("To není to, co to není, je to toto!".find("to"))            # index prvního výskytu podstringu
print("To není to, co to není, je to toto!".replace("není", "JE")) # nahrazení všech výskytů podstringu

print("   Adam Novák \t\n \n".strip())                             # ořezání stringu

print("To není to, co to není, je to toto!".upper())               # jednoduché formátování
This is a sentence.
['This', 'is', 'a', 'sentence.', 'This', 'is', 'another', 'sentence.']
False True
5
8
To JE to, co to JE, je to toto!
Adam Novák
TO NENÍ TO, CO TO NENÍ, JE TO TOTO!

Ukázali jsme si možnosti pokročilého formátování řetězců pomocí formátovacích řetězeců (f-string-ů) a pomocí metody format. Pomocí nich můžeme vytvářet textové šablony, například:

In [2]:
den = 20
měsíc = "listopad"
koncovka = "u"
skupina1 = 15
skupina2 = 21

string = f"Dnes je {den}.{měsíc}{koncovka}. Na cvičení dorazilo {skupina1 + skupina2} studentů."
print(string)

šablona = "Datum: {}.{}{}. Na cvičení dorazilo {} studentů."
dnes = šablona.format(den, měsíc, koncovka, skupina1 + skupina2)
včera = šablona.format(den - 1, měsíc, koncovka, 16)
print(dnes)
print(včera)
Dnes je 20.listopadu. Na cvičení dorazilo 36 studentů.
Datum: 20.listopadu. Na cvičení dorazilo 36 studentů.
Datum: 19.listopadu. Na cvičení dorazilo 16 studentů.

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 [1]:
# ukázka for cyklu pro list:
s = [1, 5, 4, 20, 2]
for x in s:
    print(x, end = " ")
1 5 4 
In [5]:
# ukázka for cyklu pro set
s = {1, 5, 4, 20, 2}
for x in s:
    print(x, end = " ")
1 2 4 5 20 

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 [6]:
my_list = [1, 5, 4]

my_iterator = iter(my_list)
print(my_iterator)
<list_iterator object at 0x7f11805363b0>
In [7]:
my_iterator = iter(my_list)

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

In [8]:
x = next(my_iterator)
print(x)
1
In [10]:
# iterátor poskytuje prvky seznamu jeden po druhém
my_iterator = iter(my_list)
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
1
5
4
In [11]:
# další volání 'next' vyvolá výjimku `StopIteration`
print(next(my_iterator))
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[11], 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 [18]:
my_list = [1, 5, 4, 7, 9]
In [26]:
# vytvořte nový iterátor:
my_iterator = iter(my_list)
In [27]:
# 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
1

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

In [28]:
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 [29]:
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 [30]:
my_list = [1, 8, 3]

iterator = iter(my_list)
while True:
    try:
        x = next(iterator)
        ... # do something        
    except StopIteration:
        break

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í.

In [12]:
# ukázka: změna seznamu během iterace, např. remove
my_list = [1, 8, 3, 6, 5, 7, 9]

for x in my_list:
    my_list.remove(x)
    
my_list
Out[12]:
[8, 6, 7]

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 [32]:
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 [33]:
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 
In [15]:
# ukázka: iterátor se postupně vyčerpává
my_list = [1, 8, 3]
iterator = iter(my_list)
print(next(iterator))

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

print("na seznam:")
print(list(iterator))
1
poprvé:
8 3 
na seznam:
[]

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 [18]:
my_list = [1, 7, 2, 6, 3, 1, 2]
reversed(my_list)
Out[18]:
<list_reverseiterator at 0x7f116ae4a920>
In [20]:
tuple(reversed(my_list))
Out[20]:
(2, 1, 3, 6, 2, 7, 1)
In [60]:
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(reversed(my_list))
print(reversed_list)
print(my_list)
2 1 3 6 2 7 1 
<list_reverseiterator object at 0x7ff53443cbb0>
[2, 1, 3, 6, 2, 7, 1]
[1, 7, 2, 6, 3, 1, 2]
In [25]:
# Ukázka: jak se iterátor postupně vyčerpává: next + for, next + list
my_tuple = (1, 7, 2, 6, 3, 9, 2)
it = reversed(my_tuple)

print("next:", next(it), next(it))

t = tuple(it)
print("tuple:", t)

print("for:")
for x in it:
    print(x, end = " ")
next: 2 9
tuple: (3, 6, 2, 7, 1)
for:
In [27]:
# Ukázka: jak se iterátor postupně vyčerpává: next + for, next + list
my_tuple = (1, 7, 2, 6, 3, 9, 2)
it = reversed(my_tuple)

print("next:", next(it), next(it))

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

t = tuple(it)
print("\ntuple:", t)
next: 2 9
for:
3 6 2 7 1 
tuple: ()

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 [40]:
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 [14]:
def is_even(number):
    return number % 2 == 0

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

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

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

print(even_numbers)
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 [44]:
def square(number):
    return number ** 2

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

# iterace přes druhé mocniny čísel ze seznamu:
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)
1 4 9 16 25 36 49 64 
[1, 4, 9, 16, 25, 36, 49, 64]
In [45]:
squares = list(map(lambda x: x ** 2, numbers))
squares
Out[45]:
[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.

In [30]:
# ukázka:
numbers = (1, 2, 3, 4, 5, 6, 7, 8)
even_numbers = tuple(filter(lambda x: x % 2 == 0, numbers))
even_numbers
Out[30]:
(2, 4, 6, 8)
In [33]:
# ukázka:
squares = list(map(lambda x: x ** 2, numbers))
print(squares)
[1, 4, 9, 16, 25, 36, 49, 64]

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

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

for name, salary in zip(names, salaries):
  print(f"{name} má plat {salary} Kč.")

list(zip(names, salaries))
Anna má plat 25000 Kč.
Bob má plat 35000 Kč.
Cyril má plat 45000 Kč.
Out[47]:
[('Anna', 25000), ('Bob', 35000), ('Cyril', 45000)]

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

In [50]:
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.values()))
print(w)
[(1, 'a', 'A'), (2, 'b', 'B'), (3, 'c', '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 [51]:
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 0x7ff51b4535b0>
Index: 0, Value: 10
Index: 1, Value: 1
Index: 2, Value: 2
Index: 3, Value: 0
[(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')]

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

Kromě funkcí, které vrací iterátory 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.
sorted(iterable, /, *, key=None, reverse=False) Vrací nový seznam obsahující všechny prvky iterovatelného objektu iterable.

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

In [54]:
# 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]
In [36]:
# any, all, sum, max, min:
numbers = (-1, 2.5, True)
print(any(numbers), all(numbers), sum(numbers), max(numbers), min(numbers))
True True 2.5 2.5 -1

Funkci sorted a význam volitelného parametru key u funkcí max, min a sorted detailněji rozebereme v následující kapitole.

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 [ ]:
# reversed
In [2]:
def palindrome(sequence):           # pomocí výřezu
    return sequence == sequence[ : : -1]

def palindrome(sequence):           # pomocí reversed
    return list(sequence) == list(reversed(sequence))

def palindrome(sequence):           # pomocí zip + reversed
   for x, y in zip(sequence, reversed(sequence)):
       if x != y:
           return False
   return True  

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

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 [ ]:
# map, sum
In [39]:
def f(s):
    return len(s) > 5
In [40]:
def palindrome(sequence):
    return list(sequence) == list(reversed(sequence))
In [44]:
word_list = ["ananas", "otto", "radar", "hello", "level", "civic", "python"]
In [42]:
# filter
list(filter( f, word_list))
Out[42]:
['ananas', 'python']
In [43]:
list(filter( palindrome, word_list))
Out[43]:
['otto', 'radar', 'level', 'civic']
In [49]:
# map
list(map( f, word_list))
Out[49]:
[True, False, False, False, False, False, True]
In [50]:
l = list(map( f, word_list))
print(l)
sum(l)
[True, False, False, False, False, False, True]
Out[50]:
2
In [46]:
sum(map(f, word_list))
Out[46]:
2
In [48]:
list(map( palindrome, word_list))
Out[48]:
[False, True, True, False, True, True, False]
In [47]:
sum(map(palindrome, word_list))
Out[47]:
4
In [4]:
def count_palindromes(sequence):
    pocet = 0
    for x in sequence:
        if palindrome(x):
            pocet += 1
    return pocet

def count_palindromes(sequence):
    return sum(map(palindrome, sequence))

def count_palindromes(sequence):             # generátorová notace
    return sum(palindrome(x) for x in sequence)

def palindromes(sequence):
    return list(filter(palindrome, sequence))

word_list = ["ananas", "otto", "radar", "hello", "level", "civic", "python"]

print(count_palindromes(word_list))
print(palindromes(word_list))
assert count_palindromes(word_list) == 4
4
['otto', 'radar', 'level', 'civic']

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 [5]:
def palindromes(sequence):
    v = []
    for x in sequence:
        if palindrome(x):
            v.append(x)
    return v

def palindromes(sequence):
    return list(filter(palindrome, sequence))

def palindromes(sequence):                   # generátorová notace
    return [ x for x in sequence if palindrome(x)]
    
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 [ ]:
# zip + volat:
def create_login(name, id):
    # lower, [:4], ljust, + 
    ....
In [51]:
# nápověda ke stringům
print("AbC".lower(), "abcdefgh"[:2], "abc" + str(3), "abc".ljust(5,'_'))
abc ab abc3 abc__
In [7]:
def create_login(name, id):
    v = name[:4].lower().ljust(4, '_')
    return v + str(id)
    
def create_login(name, id):
    v = (name + "____")[:4].lower()
    return v + str(id)
    
def create_logins(names, ids):
    v = []
    for name, id in zip(names, ids):
        v.append(create_login(name, id))
    return v

def create_logins(names, ids): # generátorová notace
    return [create_login(name, id) for name, id in zip(names, ids)]

names = ["John", "Alice", "Eduard", "Bob", "Ed"]
ids = [143, 787, 546, 165, 798]
print(create_logins(names,ids) )
assert create_logins(names,ids) == ['john143', 'alic787', 'edua546', 'bob_165', 'ed__798']
['john143', 'alic787', 'edua546', 'bob_165', 'ed__798']

3a. 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]

3b. 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]

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 [3]:
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 [4]:
# stringy:
my_set = {"abc", "abd", "ABC", "aBc", "ab", "abČ"}

sorted_list = sorted(my_set)
print(sorted_list)
['ABC', 'aBc', 'ab', 'abc', 'abd', 'abČ']
In [6]:
# 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 [7]:
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)
[2, 3, 4]
['a', 'b', 'c']
[(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 [53]:
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))
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']
In [55]:
"AbcD".lower()
Out[55]:
'abcd'
In [54]:
print("with key:   ", sorted(my_list, key=str.lower))
with key:    ['a', 'and', 'Anna', 'beautiful', 'Bob', 'Brighton', 'buy', 'decided', 'house', 'in', 'to']
In [10]:
print("sort by lenghts:", sorted(my_list, key=len))
sort by lenghts: ['a', 'to', 'in', 'and', 'Bob', 'buy', 'Anna', 'house', 'decided', 'Brighton', 'beautiful']
In [57]:
# další ukázka
def f(x):
    return x[-1]
print(sorted(my_list, key=f))

print(sorted(my_list, key=lambda x: x[-1]))
['Anna', 'a', 'Bob', 'and', 'decided', 'house', 'beautiful', 'in', 'Brighton', 'to', 'buy']
['Anna', 'a', 'Bob', 'and', 'decided', 'house', 'beautiful', 'in', 'Brighton', 'to', 'buy']

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

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

sorted_list = sorted(my_tuples)
print(sorted_list)

def second(x):
    return x[1]

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

# setřídění podle druhé hodnoty v n-tici:
sorted_list = sorted(my_tuples, key=lambda x : x[1])
print(sorted_list)
[(3, 'ba', False), (3, 'bc', True), (4, 'bba', True)]
[(3, 'ba', False), (4, 'bba', True), (3, 'bc', True)]
[(3, 'ba', False), (4, 'bba', True), (3, 'bc', 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 [13]:
my_list = [-5, 2, -3, 1, 4]

v = my_list.sort()
print(v, my_list)

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

my_list.sort(reverse=True, key=abs)
print(my_list)
None [-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]

Modul itertools¶

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 [14]:
import itertools

# permutace:
for x in itertools.permutations("abcd"):
    print(x, end=" ")
print("")
('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') 
In [59]:
import itertools

# permutace:
for x in itertools.permutations([1, 3, 7]):
    print(x, end=" ")
print("")
(1, 3, 7) (1, 7, 3) (3, 1, 7) (3, 7, 1) (7, 1, 3) (7, 3, 1) 
In [29]:
import itertools

# permutace:
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=" ")
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 
  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 [21]:
login_list = ["john1234", "alice5678", "bob2345", "eve8765"]
sorted(login_list, key=len)
Out[21]:
['bob2345', 'eve8765', 'john1234', 'alice5678']
The history saving thread hit an unexpected error (OperationalError('attempt to write a readonly database')).History will not be written to the database.
In [23]:
def second(x):
    return x[1]
sorted(login_list, key=second)
Out[23]:
['alice5678', 'john1234', 'bob2345', 'eve8765']
In [24]:
sorted(login_list, key=lambda x : x[1])
Out[24]:
['alice5678', 'john1234', 'bob2345', 'eve8765']
In [25]:
def last4(login):
    return login[-4:]
sorted(login_list, key=last4)
Out[25]:
['john1234', 'bob2345', 'alice5678', 'eve8765']
In [27]:
def sort_logins_by_id(logins):
    return sorted(logins, key= last4, reverse = True)
    
def sort_logins_by_id(logins):
    return sorted(logins, key= lambda x: x[-4:], 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"]

5a. 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 [30]:
def distance(vector):
    return (vector[0] ** 2 + vector[1] ** 2 + vector[2] ** 2) ** 0.5 
    
def sort_vectors_by_distance(vectors):
   return sorted(vectors, key = distance)

def sort_vectors_by_distance(vectors):
   return sorted(vectors, key = lambda vector: (vector[0] ** 2 + vector[1] ** 2 + vector[2] ** 2) ** 0.5 )

def distance(vector):
    dist = 0
    for x in vector:
        dist += x**2
    return dist ** 0.5 
    
def sort_vectors_by_distance(vectors):
   return sorted(vectors, key = distance)
    
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)]

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

In [62]:
s = [10, 2, 6]
s.sort()
s
Out[62]:
[2, 6, 10]
In [31]:
def sort_vectors_by_distance(vectors):
   vectors.sort(key= distance)

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 [ ]:
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]}]

6a. 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 [ ]:
def sort_students_by_average_grade(students):
   ...
   
sort_students_by_average_grade(grades)
Jakub: Průměr známek: 1.40
Martin: Průměr známek: 1.67
Adam: Průměr známek: 1.80
Filip: Průměr známek: 2.00
David: Průměr známek: 2.00

6b. 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']
  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 [5]:
chars = "0123456789"
def evaluate_password(string):
    ''' Returns True if string == passwd
    '''
    passwd = chars[-1] * n
    return string == passwd

evaluate_password("9999"), evaluate_password("2014")
Out[5]:
(True, False)
In [76]:
chars = "0123456789"
chars = "0123456789abcdefghijklmnopqrstuvwxyz"
chars = "012"
n = 4

def evaluate_password(string):
    ''' Returns True if string == passwd
    '''
    passwd = chars[-1] * n
    #passwd = "bgda"
    return string == passwd
        
def try_password(chars, n): # první nápad:
    for x in itertools.combinations_with_replacement(chars, n):
        z = "".join(x)
        print(z, end = " ")

try_password(chars, n) # nějaká hesla chybí
0000 0001 0002 0011 0012 0022 0111 0112 0122 0222 1111 1112 1122 1222 2222 
In [75]:
chars = "0123456789"
chars = "0123456789abcdefghijklmnopqrstuvwxyz"
chars = "012"
n = 4

def evaluate_password(string):
    ''' Returns True if string == passwd
    '''
    passwd = chars[-1] * n
    #passwd = "bgda"
    return string == passwd
        
def try_password(chars, n): # lepší nápad:
    for x in itertools.combinations_with_replacement(chars, n):
        for y in itertools.permutations(x):
            z = "".join(y)
            print(z, end = " ")

try_password(chars, n) # nějaká hesla se zbytečně opakují
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0010 0001 0010 0100 0100 0001 0010 0001 0010 0100 0100 0001 0010 0001 0010 0100 0100 1000 1000 1000 1000 1000 1000 0002 0020 0002 0020 0200 0200 0002 0020 0002 0020 0200 0200 0002 0020 0002 0020 0200 0200 2000 2000 2000 2000 2000 2000 0011 0011 0101 0110 0101 0110 0011 0011 0101 0110 0101 0110 1001 1010 1001 1010 1100 1100 1001 1010 1001 1010 1100 1100 0012 0021 0102 0120 0201 0210 0012 0021 0102 0120 0201 0210 1002 1020 1002 1020 1200 1200 2001 2010 2001 2010 2100 2100 0022 0022 0202 0220 0202 0220 0022 0022 0202 0220 0202 0220 2002 2020 2002 2020 2200 2200 2002 2020 2002 2020 2200 2200 0111 0111 0111 0111 0111 0111 1011 1011 1101 1110 1101 1110 1011 1011 1101 1110 1101 1110 1011 1011 1101 1110 1101 1110 0112 0121 0112 0121 0211 0211 1012 1021 1102 1120 1201 1210 1012 1021 1102 1120 1201 1210 2011 2011 2101 2110 2101 2110 0122 0122 0212 0221 0212 0221 1022 1022 1202 1220 1202 1220 2012 2021 2102 2120 2201 2210 2012 2021 2102 2120 2201 2210 0222 0222 0222 0222 0222 0222 2022 2022 2202 2220 2202 2220 2022 2022 2202 2220 2202 2220 2022 2022 2202 2220 2202 2220 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1112 1121 1112 1121 1211 1211 1112 1121 1112 1121 1211 1211 1112 1121 1112 1121 1211 1211 2111 2111 2111 2111 2111 2111 1122 1122 1212 1221 1212 1221 1122 1122 1212 1221 1212 1221 2112 2121 2112 2121 2211 2211 2112 2121 2112 2121 2211 2211 1222 1222 1222 1222 1222 1222 2122 2122 2212 2221 2212 2221 2122 2122 2212 2221 2212 2221 2122 2122 2212 2221 2212 2221 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 2222 
In [2]:
# nejlepší řešení:
import itertools
chars = "0123456789"
chars = "0123456789abcdefghijklmnopqrstuvwxyz"
chars = "012"
n = 4

def evaluate_password(string):
    ''' Returns True if string == passwd
    '''
    passwd = chars[-1] * n   # "zzzz"
    #passwd = "bgda"
    return string == passwd
        
def try_password(chars, n):
    for tup in itertools.product(chars, repeat=n):  # variace s opakováním
        z = "".join(tup)
        print(z, end = " ")

try_password(chars, n) # teď je to akorát
0000 0001 0002 0010 0011 0012 0020 0021 0022 0100 0101 0102 0110 0111 0112 0120 0121 0122 0200 0201 0202 0210 0211 0212 0220 0221 0222 1000 1001 1002 1010 1011 1012 1020 1021 1022 1100 1101 1102 1110 1111 1112 1120 1121 1122 1200 1201 1202 1210 1211 1212 1220 1221 1222 2000 2001 2002 2010 2011 2012 2020 2021 2022 2100 2101 2102 2110 2111 2112 2120 2121 2122 2200 2201 2202 2210 2211 2212 2220 2221 2222 
In [73]:
import itertools
chars = "0123456789"
chars = "0123456789abcdefghijklmnopqrstuvwxyz"
n = 4

def evaluate_password(string):
    ''' Returns True if string == passwd
    '''
    passwd = chars[-1] * n   # "zzzz"
    passwd = "bgda"
    return string == passwd
        
def guess_password(chars, n):
    for tup in itertools.product(chars, repeat=n):  # variace s opakováním
        z = "".join(tup)
        if evaluate_password(z):
            return z

passwd = guess_password(chars, n)
print("Finished:", passwd)
Finished: bgda
In [ ]: