![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Dosud poznané složené datové typy – řetězce, seznamy a entice – jsou sekvence, které používají celá čísla jako indexy pro přístup k jednotlivým položkám.
Slovník (dictionary, dict), je uspořádaná (Python > 3.7) a měnitelná kolekce párových položek key : value. Klíčem může být pouze neměnitelný datový typ, hodnotou libovolný datový typ.
Jako příklad vytvoříme slovník pro překlad anglických slov do španělštiny. Klíči i hodnotami u tohoto slovníku budou řetězce.
Začneme tak, že vytvoříme prázdný slovník, do kterého přidáme párové položky. Prázdný slovník se označuje {}:
>>> en2sp = {} >>> en2sp['one'] = 'uno' >>> en2sp['two'] = 'dos' >>> en2sp {'one': 'uno', 'two': 'dos'}
První přiřazení vytvoří prázdný slovník s názvem
Jiným způsobem vytvoříme slovník tak, že přímo zadáme výčet slovníkových párů:
>>> en2sp = {'one': 'uno', 'two': 'dos', 'three': 'tres'}
Případně pro vytvoření slovníku použijeme vestavěnou funkci
>>> di = dict(); di {} >>> dj = dict(a=10, b=20); dj {'a': 10, 'b': 20} >>> dk = dict([('foo', 100), ('bar', 200)]); dk {'foo': 100, 'bar': 200}Poznámka: funkce 'dict()' očekává pouze 1 argument; proto je nutné výpis entic (u dk) uzavřít do dalšího páru závorek (typu list, tuple, set).
Hodnoty ve slovníku jsou přístupné prostřednictvím klíčů:
>>> en2sp['two'] 'dos'
Klíčový operátor použijeme také pro přidání nové dvojice na konec slovníku:
>>> en2sp['four'] = "quatro"; en2sp {'one': 'uno', 'two': 'dos', 'three': 'tres', 'four': 'quatro'}
Při výskytu dvou zadaných stejných klíčů se uplatní jen ten později zadaný:
>>> en2sp['two'] = "duo"; en2sp {'one': 'uno', 'two': 'duo', 'three': 'tres', 'four': 'quatro'}
Příkaz
>>> ovoce = {'apples': 430, 'bananas': 312, 'pears': 217, 'oranges': 525} >>> ovoce {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217}
Když někdo skoupí všechny hrušky, můžeme tento vstup ze slovníku vyjmout:
>>> del ovoce['pears']; ovoce {'apples': 430, 'bananas': 312, 'oranges': 525}
Nebo očekáváme-li, že hrušky zase budou, můžeme pouze změnit hodnotu spojenou s hruškami:
>>> ovoce['pears'] = 0; ovoce {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 0}
U slovníku můžeme také použít funkci
>>> len(ovoce) 4
Slovníky mají řadu užitečných vestavěných metod, jejichž seznam získáme známou funkcí
>>> dir(dict) ['__class__', '...', 'clear', 'copy', 'fromkeys', 'get', 'items',
'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
Metoda
>>> car = {"značka": "Ford", "model": "Mustang","rok": 1964} >>> car.update({"color": "black"}); car {'značka': 'Ford', 'model': 'Mustang', 'rok': 1964, 'color': 'black'}
Metoda
>>> car = {"značka": "Ford", "model": "Mustang","rok": 1964} >>> car.get("model") 'Mustang'
Metoda
>>> car.items() dict_items([('značka', 'Ford'), ('model', 'Mustang'), ('rok', 1964)])
Metoda
>>> car.keys() dict_keys(['značka', 'model', 'rok'])
Metoda
>>> car.values() dict_values(['Ford', 'Mustang', 1964])
Výpis
>>> for i in car: print(i, end=' ') značka model rok
Výpis
>>> for i in car.(values): print(i, end=' ') Ford Mustang 1964
K rozbalení klíčů, hodnot i položek slovníku můžeme také použít
>>> [*car] ['značka', 'model', 'rok'] >>> [*car.keys()] ['značka', 'model', 'rok'] >>> [*car.values()] ['Ford', 'Mustang', 1964] >>> [*car.items()] [('značka', 'Ford'), ('model', 'Mustang'), ('rok', 1964)]
Přítomnost klíče ve slovníku lze ověřit idiomem s klíčovým slovem
>>> "model" in car True >>> "barva" in car False
Verze Python 3.9.0 přináší dva nové operátory pro sloučení slovníků,
Sjednocení | sloučí obsah obou operandů (zde slovníků). Nachází-li se stejný klíč v obou operandech, pro sloučení se vybere jeho hodnota z pravého operandu - viz případ
>>> d = {'spam': 1, 'vejce': 2, 'sýr': 3} >>> e = {'sýr': 'čedar', 'sele': 'Pepík'} >>> d | e {'spam': 1, 'vejce': 2, 'sýr': 'čedar', 'sele': 'Pepík'} >>> e | d {'sýr': 3, 'sele': 'Pepík', 'spam': 1, 'vejce': 2}
Rozšířené přiřazení provede přiřazení se sjednocením:
>>> d |= e# totéž jako d = d | e >>> d {'spam': 1, 'vejce': 2, 'sýr': 'čedar', 'sele': 'Pepík'} >>> e {'sýr': 'čedar', 'sele': 'Pepík'}
Rozšířené přiřazení lze použít ke změně přiřazené hodnoty:
>>> d |= [('spam', 999)]; d {'spam': 999, 'vejce': 2, 'sýr': 'čedar', 'sele': 'Pepík'}
Případně k vložení nového páru:
>>> d |= [('ham', 777)]; d {'spam': 999, 'vejce': 2, 'sýr': 'čedar', 'sele': 'Pepík', 'ham': 777}
Komprehence slovníku se příliš neliší od komprehence seznamu. Výstupem z komprehence slovníku je slovník, vytvořený aplikací zadaného výrazu pro každý element iteráblu.
{key: valuefor (key [, value])in iterable}
Iteráblem zde může dle okolností být objekt typu
>>> dct_range= {x: x**2 for x in range(5)}; dct_range {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}Totéž pomocí mrožího operátoru: >>> print(dct_range:= {x: x**2 for x in range(5)}) {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
Při komprehenci slovníku ze dvou iteráblů můžeme použít i funkci
>>> keys = ['a', 'b', 'c'] >>> vals = [1, 2, 3] >>> print(dict_compr_zip := {keys:vals \ ... for (keys,vals) inzip (keys, vals)}) {'a': 1, 'b': 2, 'c': 3} >>> dict_compr_zip# vytvořenou proměnnou lze nezávisle použít {'a': 1, 'b': 2, 'c': 3}
Slovník vytvoříme i z jednoho iteráblu(tuple, list) funkcemi
>>> a = ['hello','world','nomen','omen'] >>> dict(zip(i := iter(a), i))# zip(i,i) {'hello': 'world', 'nomen': 'omen'}
Protože jsou slovníky měnitelné, musíme si dát pozor na aliasování. Kdykoliv dvě proměnné odkazují ke stejnému objektu, jeho změna prostřednictvím jedné proměnné je sdílena i druhou proměnnou.
Chceme-li měnit slovník a přitom si zachovat kopclr-ii originálu, použijeme metodu
>>> oppos = {'up': 'down', 'right': 'wrong', 'true': 'false'} >>> alias = opposid(alias) == id(oppos) >>> kopie = oppos.copy()id(kopie) != id(oppos)
Proměnné
>>> alias['right'] = 'left' >>> oppos['right'] 'left'
Když naproti tomu upravíme
>>> kopie['right'] = 'privilege' >>> oppos['right'] 'left'
K prezentaci matice lze použít seznam seznamů. To je dobrý způsob pro matice s převážně nenulovými hodnotami, uvažme však řídkou matici jako je tato:
Vyjádření řídké matice prostřednictvím seznamu obsahuje množství nul:
matrix = [ [0,0,0,1,0], [0,0,0,0,0], [0,2,0,0,0], [0,0,0,0,0], [0,0,0,3,0] ]Poloha prvku v matici se vyjádří enticí (poř_číslo_řádku,
poř_číslo_sloupce). Číslování počíná nulou.
Alternativou je použití slovníku. Jako klíče můžeme použít entice, obsahující pořadová čísla řádků a sloupců. Zde je slovníkové vyjádření téže matice:
>>> matrix = {(0,3): 1, (2,1): 2, (4,3): 3}
Nenulové prvky matice jsou přístupné pomocí operátoru [ ]:
>>> matrix[0,3]0,3 je souřadnice mista 1
Pokud však narazíme na nulový prvek matice, dostaneme chybu:
>>> matrix[1,3]KeyError: (1, 3)
Tento problém řeší metoda
>>> matrix.get ((0,3),0) 1 >>> matrix.get ((1,3),0) 0
Výběrovou proceduru switch case či dispatch lze v Pythonu vytvořit pomocí slovníku. Místo sekvence podmínek
def func_a(): print("fce-a") def func_b(): print("fce-b") def func_c(): print("fce-c") oslík = {"a": func_a, "b": func_b, "c": func_c} def print_f(value):# Včetně částečného ošetření výjimek oslík.get(value, lambda: print("Neplatné!"))()
Výběr funkce provedeme voláním funkce
>>> print_f("b") fce-b >>> print_f(5) Neplatné >>> oslík["c"]() # Kulaté závorky dotvářejí volání funkce. fce-c
Volané funkce mohou být také deklarovány jako vložené funkce lambda:
def kalkul (operátor, x, y): cases = { "+": lambda a, b: a + b, "-": lambda a, b: a - b, "*": lambda a, b: a * b, "/": lambda a, b: a / b, }# Deklarované případy: if operátorin cases.keys():return cases[operátor](x, y)# Částečné ošetření výjimek else :return cases.get(operátor, "Nenalezeno!")
>>> kalkul('/', 5, 2) 2.5 >>> kalkul(# Viz poznámka Nenalezeno! >>> calcul(^, 5, 3)SyntaxError: invalid syntax
Variace: Funkce lambda lze zajisté nahradit funkcemi pro operace +, -, *, a /. Příslušný výraz se nahradí voláním odpovídající funkce, například:
def add(x, y): return x + y def sub(x, y): return x - y def mul(x, y): return x * y def div(x, y): return x / y def kalk(operátor, x, y): cases = {"+": add, "-": sub, "*": mul, "/": div} return cases[operátor](x, y)
Simulaci přepínače lze také vytvořit prostřednictvím třídy.
V kapitole 6.17, cvičení 2 a 3 jsme počítali výskyt zadaného znaku v zadaném řetězci a to pomocí smyčky s počítadlem, případně přímo prostřednictvím metody 'count'.
Také jsme v kap.9.4 počítali výskyty náhodných čísel v jednolivých úsecích rozpětí 0.0 až 1.0 jako potvrzení skutečnosti, že funkce 'random' modulu 'random' generuje pseudonáhodná čísla.
V následujícím komentovaném programu
# countLetters.py# Ošetření tisknutelných (chr(i)) i netisknutelných znaků: def display (i):if i == 10:return 'LF'if i == 13:return 'CR'if i == 32:return 'SPACE'return chr(i)# vrací tisknutelný znak. Tato funkce bude opakovaně použita v následné smyčce. Argument 'i' je UCP (Unicode Code Point) zkoumaného znaku. # Načtení textu ze souboru do paměti interpreta: inpath = '../files_txt/alice_wonderland.txt'# cesta k souboru infile = open(inpath, encoding="utf-8") text = infile.read()# 'text' má formát řetězce infile.close()Pro anglický text (kódování ASCii) lze zadat délku seznamu counts = 127 * [0], pro český text (UTF-8) je nutné zadat counts = 383 * [0] Případně pokud umíme určit max UCP jako zde: >>> text = "abcde\n efgh" >>> ord(max(text)) --> 104 --> counts = 105 * [0] můžeme použít seznam kratší. Položky seznamu 'counts' jsou receptory výskytů jednotlivých písmen. Zjištěné hodnoty UCP slouží jako indexy seznamu # Načtení výskytů do seznamu 'counts': counts = 127 * [0]# max UCP pro ASCii = 126 for letterin text: counts[ord(letter)] += 1# přičítá výskyty znaků # ord(letter) je aktuální index seznamu 'counts' # Strukturovaný zápis výskytů do souboru 'letter_counts.dat': outpath = '../files_dat/letter_counts.dat'# deklarace cesty outfile = open(outpath, 'w')# otevření/vytvoření souboru # formátované záhlaví souboru: outfile.write("%-12s%s\n" % ("Character", "Count")) outfile.write("=================\n")# formátovaný obsah pod záhlavím: for iin range(len(counts)):# len(counts) = 126+1 if counts[i]:# if counts[i] != 0 outfile.write("%-12s%d\n" % (display(i), counts[i])) outfile.close()# povinné zavření otevřeného souboru Funkce 'range' v proceduře 'range(len(counts))' je důvodem ke zvýšení délky seznmu 'count' o 1. # Toto oznámení se vytiskne v konzole interpreta IDLE: >>># Frekvenční tabulku si prohlédneme v souboru 'letter_counts.dat'.
Spusťte si tento program v IDLE a prohlédněte si generovaný výstupní soubor v textovém editoru.
Pomocí slovníku vytvoříme seznam výskytů znaků elegantním způsobem:
>>> letter_counts = {}# <class 'dict'> >>>for leclr-tterin "Mississippi": ... letter_counts[letter] = letter_counts.get (letter, 0) + 1 >>> letter_counts {'M': 1, 'i': 4, 's': 4, 'p': 2}
Začínáme prázdným slovníkem. Pro každé písmeno v řetězci zvětšíme aktuální četnost o 1. Na konci procesu máme slovník obsahující dvojice písmen a jejich četností.
Bylo by ještě působivější, kdybychom výskyt písmen uspořádali podle abecedy. Můžeme to udělat pomocí
univerzální funkce
>>> sorted(letter_counts) ['M', 'i', 'p', 's'] >>> sorted(letter_counts.items()) [('M', 1), ('i', 4), ('p', 2), ('s', 4)]
Proč je
>>> ord("M"), ord("i") (77, 105)
Výskyt zadaného slova ve vstupním řetězci určíme jednoduše metodou
text = "apple mango apple orange orange apple guava\ mango mango"def word_count(text, word):str (text.count (word)))
>>> word_count(text, "mango") mango 3
Nejjednodušší způsob určení výskytu slov v textu je metodou
text = "Woodchuck, how much wood would a woodchuck \ chuck if a woodchuck could chuck wood ?" sett =set ()# prázdný kolektor typu 'set' lstt = text.split ()# konverze stringu na list for iin lstt: sett.add ((i, lstt.count (i)))# 'add' přijímá 1 arg.
S výstupem coby slovník:
>>> print(dict(sett))# konverze setu na slovník {'a': 2, 'would': 1, 'how': 1, 'much': 1, 'could': 1, 'chuck': 2, 'Woodchuck,': 1, 'wood': 2, 'woodchuck': 2, '?': 1, 'if': 1}
Případně coby seznam:
>>> print(list(sett))# konverze setu na seznam [('?', 1), ('chuck', 2), ('woodchuck', 2), ('would', 1), ('Woodchuck,', 1), ('how', 1), ('wood', 2), ('could', 1), ('much', 1), ('a', 2), ('if', 1)]
def letter_counts(retiazka): ...
Výstup pro "Mississippi" má tvar:
>>> letter_counts("Mississippi") {'M': 1, 'i': 4, 'p': 2, 's': 4}
Pokuste se přeformulovat předchozí úlohu tak aby výstupní frekvenční tabulka pro "Mississippi" měla formát:
M 1 i 4 s 4 p 2
Pro formulaci výstupu můžete použít postup, použitý v kap. 11.8. Nezapomeňte si prohlédnout generovaný výstupní soubor
>>> d = {"apples":15, "bananas":35, "grapes":12} >>> d["bananas"]
>>> d["oranges"] = 20
>>> len(d)
>>> "grapes" in d
>>> d["pears"]
>>> d.get("pears", 0)
>>> fruits = d.keys() >>> sorted(fruits)
>>> del d["apples"] >>> "apples" in d
def add_fruit(inventory, fruit, quantity=0): pass# tyto testy by měly vyjít new_inventory = {} add_fruit(new_inventory, 'strawberries', 10) test('strawberries' in new_inventory, True) test(new_inventory['strawberries'], 10) add_fruit(new_inventory, 'strawberries', 25) test(new_inventory['strawberries'] , 35)
Word Count ======================= a 631 a-piece 1 abide 1 able 1 about 94 above 3 absence 1 absurd 2Kolikrát se v knize objeví slovo
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |