Subsections

 
5. Datové struktury

V této kapitole si zopakujeme a prohloubíme znalosti o datových typech, které můžeme v jazyce Python používat.

 
5.1 Seznamy

Datový typ seznam má kromě již uvedených metod i mnoho dalších, o kterých jsme se zatím nezmiňovali. Jsou to:

append(x)
Přidá prvek na konec seznamu. Tato metoda je ekvivalentní zápisu a[len(a):] = [x].

extend(L)
Rozšíří seznam přidáním všech prvků ze seznamu L na konec původního seznamu, ekvivalentní a[len(a):] = L.

insert(i, x)
Vloží nový prvek na zadanou pozici. První argument je index prvku, před který se má nová položka vložit, takže a.insert(0, x) vloží prvek na začátek seznamu a a.insert(len(a), x) je ekvivalentní a.append(x).

remove(x)
Ze seznamu odstraní první prvek, jehož hodnota je rovna x. Není-li prvek v seznamu nalezen, dojde k výjimce.

pop([i])
Odstraní prvek na určené pozici v seznamu a vrátí jeho hodnotu. Jestliže není zadán žádný index, a.pop() odstraní a vrátí hodnotu posledního prvku seznamu.

index(x)
Vrátí index prvního prvku s hodnotou x v seznamu. Není-li prvek v seznamu nalezen, dojde k výjimce.

count(x)
Vrátí počet výskytů prvků s hodnotou x v celém seznamu.

sort()
Seřadí prvky seznamu podle velikosti. Seřazení se děje na původním seznamu.

reverse()
Převrátí pořadí prvků - první je poslední, druhý předposlední. Změna se děje opět na původním seznamu.

Příklad, který ukazuje jak používat většinou metod seznamů:

>>> a = [66.6, 333, 333, 1, 1234.5]
>>> print a.count(333), a.count(66.6), a.count('x')
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.6, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.6, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.6]
>>> a.sort()
>>> a
[-1, 1, 66.6, 333, 333, 1234.5]

 
5.1.1 Seznamy jako zásobníky

Metody append() a pop() činí ze seznamů snadno použitelné zásobníky typu LIFO - poslední přidaný prvek je odebrán jako první. Pro přidání prvku na vrchol zásobníku použijeme metodu append() a pro odebrání prvku z vrcholu zásobníku pak metodu pop() bez explicitního udání indexu. Například:

>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]

 
5.1.2 Seznamy jako fronty

Stejně jednoduše lze seznamy použít i jako fronty FIFO - první vložený prvek je odebrán také jako první. Pro přidání prvku na konec frony použijeme metodu append(). Pro získání prvního prvku z fronty použijeme metodu pop() s indexem 0. Například:

>>> fronta = ["Python", "Perl", "PHP"]
>>> fronta.append("Ruby")
>>> fronta.append("Lisp")
>>> fronta.pop(0)
'Python'
>>> fronta.pop(0)
'Perl'
>>> fronta
['PHP', 'Ruby', 'Lisp']

 
5.1.3 Funkční programování v jazyce Python

Existují tři interní funkce, které jsou velice užitečné pro manipulace se seznamy: filter(), map() a reduce().

"filter(funkce, sekvence)" vrátí sekvenci stejného typu jako sekvence složenou z těch prvků, pro které je funkce(prvek) pravdivým logickým výrazem. Příklad pro výpočet některých prvočísel:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

"map(funkce, sekvence)" zavolá funkci funkce(prvek) pro každý prvek sekvence sekvence a vrátí seznam vrácených hodnot. Například výpočet druhých mocnin:

>>> def cube(x): return x*x*x
...
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

Funkci map() může být předáno více než jedna sekvence, funkce pak musí mít tolik argumentů, kolik je použito sekvencí a je volána s hodnotami odpovídajícím prvkům z jednotlivých sekvencí (nebo s hodnotou None, jestliže jedna sekvence je kratší než druhá). Jestliže na místě funkce je předána hodnota None, je za argument funkce dosazena funkce vracející své argumenty.

Zkombinováním těchto dvou speciálních případů získáme volání "map(None, seznam1, seznam2)", které je konvencí pro převedení dvojice seznamů na seznam dvojic. Například:

>>> seq = range(8)
>>> def square(x): return x*x
...
>>> map(None, seq, map(square, seq))
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49)]

"reduce(funkce, sekvence)" vrátí jedinou hodnotu získanou opakovaným voláním funkce funkce, které jsou nejprve předány první dva prvky sekvence, pak její výsledek a další prvek atd. Takto lze vypočítat třeba součet čísel od 1 do 10:

>>> def add(x,y): return x+y
...
>>> reduce(add, range(1, 11))
55

Jestliže sekvence obsahuje jen jediný prvek, je vrácena hodnota tohoto prvku, jestliže je sekvence prázdná, dojde k výjimce.

Funkci reduce() může být předán ještě třetí argument indikující počáteční hodnotu. V tomto případě je pro prázdnou sekvenci vrácena tato počáteční hodnota. Poprvé je funkce aplikována na počáteční hodnotu a první prvek, pak na výsledek a další prvek atd. Například:

>>> def sum(seq):
...     def add(x,y): return x+y
...     return reduce(add, seq, 0)
... 
>>> sum(range(1, 11))
55
>>> sum([])
0

5.1.4 Konstruktor seznamu

Konstruktor seznamu je standardní cestou k vytvoření seznamu bez použití funkcí map(), filter() a nebo anonymních funkcí. Výsledná definice seznamu je mnohdy čistější než seznam vytvořený za použití výše uvedených fukcí. Každý konstruktor seznamu sestává z výrazu následovaným klauzulí for a žádnou nebo více for nebo if klauzulí. Výsledkem bude seznam získaný vyhodnocením výrazu v kontextu klauzulí for a if, které ho následují. Jestliže výraz má být vyhodnocen jako tuple, musí být uzávorkován.

>>> ovoce = ['  banán', '  rajče ', 'protlak  ']
>>> [zbran.strip() for zbran in ovoce]
['banán', 'rajče', 'protlak']
>>> vec = [2, 4, 6]
>>> [3*x for x in vec]
[6, 12, 18]
>>> [3*x for x in vec if x > 3]
[12, 18]
>>> [3*x for x in vec if x < 2]
[]
>>> [{x: x**2} for x in vec]
[{2: 4}, {4: 16}, {6: 36}]
>>> [[x,x**2] for x in vec]
[[2, 4], [4, 16], [6, 36]]
>>> [x, x**2 for x in vec]	# CHYBA - jsou vyžadovány závorky
  File "<stdin>", line 1, in ?
    [x, x**2 for x in vec]
               ^
SyntaxError: invalid syntax
>>> [(x, x**2) for x in vec]
[(2, 4), (4, 16), (6, 36)]
>>> vec1 = [2, 4, 6]
>>> vec2 = [4, 3, -9]
>>> [x*y for x in vec1 for y in vec2]
[8, 6, -18, 16, 12, -36, 24, 18, -54]
>>> [x+y for x in vec1 for y in vec2]
[6, 5, -7, 8, 7, -5, 10, 9, -3]
>>> [vec1[i]*vec2[i] for i in range(len(vec1))]
[8, 12, -54]

 
5.2 Příkaz del

Příkaz del je možné použít k odstranění prvku ze seznamu na určeném indexu, aniž bychom byli nuceni znát jeho hodnotu (což vyžaduje metoda remove(x)). Tento příkaz může být také použit pro odstranění určité slice ze seznamu (dříve jsme to dělali přiřazením prázdného seznamu této slice). Například:

>>> a
[-1, 1, 66.6, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.6, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.6, 1234.5]

Příkaz del může být také použit pro odstranění celých proměnných:

>>> del a

Odkazování se na jméno a po tomto příkazu vede k chybě (tedy pokud mu nebyla znovu přiřazena nějaká hodnota). Další použití příkazu del uvidíme později.

 
5.3 Tuple a sekvence

Jak jsme viděli, seznamy a řetězce mají mnoho společných vlastností jako možnost indexování nebo slice konstrukce. Seznamy a řetězce jsou příkladem sekvenčních datových typů. Python podporuje ještě jeden standardní sekvenční datový typ - tuple. Poněvadž se jazyk Python stále vyvíjí, mohou být v budoucny přidány další.

Tuple se sestává z několika hodnot oddělených čárkou, třeba:

>>> t = 12345, 54321, 'ahoj!'
>>> t[0]
12345
>>> t
(12345, 54321, 'ahoj!')
>>> # Tuple mohou být skládány:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'ahoj!'), (1, 2, 3, 4, 5))

Jak jste viděli, zobrazené tuple jsou vždy uzavřeny do závorek. Vložené tuple jsou samozřejmě intepretovány korektně. Tuple mohou být zadávány s nebo bez obklopujících závorek, závorky jsou však většinou vyžadovány, je-li tuple součástí většího výrazu.

Tuple mají mnoho použití. Například páry souřadnic (x, y), záznamy o zaměstancích v databázi apod. Tuple, podobně jako řetězce, jsou neměnné -- není možné přiřadit hodnotu určitému prvku tuple (můžete si však vypomoci slice konstrukcemi a spojením výsledných řezů). Také je možné vytvořit tuple, které obsahují proměnné objekty - třeba seznamy.

Speciálním případem je vytváření prázdné tuple a tuple obsahující jeden prvek. Prázdná tuple se vytvoří prázdným párem závorek, tuple s jedním prvkem získáme zápisem hodnoty následované čárkou (není nutné ji uzavírat do závorek). Například:

>>> prazdna = ()
>>> singleton = 'ahoj',    # <-- nezapomeňte ukončující čárku
>>> len(prazdna)
0
>>> len(singleton)
1
>>> singleton
('ahoj',)

Výraz t = 12345, 54321, 'ahoj!' je příkladem skládání tuple: hodnotu 12345, 54321 a 'ahoj' jsou složeny v tuple. Opačná operace je také možná:

>>> x, y, z = t

Ta je nazývána, překvapivě, rozklad sekvencí. Rozklad sekvencí vyžaduje, aby seznam proměnných na levé straně byl stejně dlouhý jako sekvence na straně druhé. Pamatujte, že vícenásobné přiřazení je pouhou kombinací skládání tuple a rozkladu sekvencí.

Existuje malá asymetrie: skládání více hodnot vždy vytvoří tuple, ale rozklad funguje pro libovolnou sekvenci.

 
5.4 Slovníky

Dalším užitečným datovým typem, kterým Python disponuje je slovník. Slovníky, někdy také nazývané "asociativní pole", jsou narozdíl od sekvencí, které jsou indexovány pomocí čísel, indexovány klíči. Klíč může být libovolný neměnný typ, např. čísla nebo řetězce. Tuple mohou být použity jako klíče pouze když obsahují jen řetězce, čísla nebo tuple, jestliže tuple přímo nebo nepřímo obsahuje proměnný datový typ, nemůže být použita jako klíč. Žádný seznam nemůže být použit jako klíč, protože seznamy mohou být modifikovány za použití jejich metod, o přiřazení hodnot jednotlivým prvkům ani nemluvě.

Nejlepší je slovník považovat jako neuspořádanou množinu párů klíč: hodnota, kde klíč musí být v rámci tohoto slovníku unikátní. Pár složených závorek vytvoří prázdný slovník: {}. Umístěním párů klíč: hodnota oddělených čárkou mezi tyto závorku vytvoříme slovník s definovanými klíči.

Slovníky podporují ukládání a čtení hodnot o zadaném klíči. Rovněž je možné smazat pár klíč: hodnota pomocí příkazu del. Jestliže uložíte novou hodnotu pod klíčem, který již slovník obsahuje, dojde k přepsání položky a stará hodnota je zapomenuta. Pokus o získání hodnoty pomocí neexistujícího klíče vede k výjimce.

Metoda keys(), kterou mají všechny slovníky, vrátí seznam všech klíčů použitých ve slovníku v náhodném pořadí (jestliže jej chcete seřazený, použijte metodu sort() výsledného seznamu). Pro kontrolu, jestli je klíč obsažen ve slovníku, použijte jeho metodu has_key().

Zde je malý příklad použití slovnkíku:

>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> tel.keys()
['guido', 'irv', 'jack']
>>> tel.has_key('guido')
1

 
5.5 Více o podmínkách

Podmínky, použité výše v konstrukcích while a if, se nemusí sestávat jen z operátorů porovnávání.

Operátory in a not in testují, jestli sekvence obsahuje (resp. neobsahuje) hodnotu. Operátory is a not is porovnávají dva objekty, jestli jsou to opravdu tytéž objekty, význam mají jen u proměnných typů jako seznamy nebo slovníky. Všechny operátory porovnání mají stejnou prioritu, která je nižší než mají všechny numerické operátory.

Porovnání může být zřetězeno. Například a < b == c testuje jestli a je menší než b a zároveň b je rovno c.

Porovnání mohou být kombinována za použití Booleových operátorů and a or a výsledek porovnání (libovolného logického výrazu) může být znegován operátorem not. Tyto všechny operátory mají ještě nižší prioritu než operátory porovnání. A mezi nimi ještě not má prioritu nejvyšší a or nejnižší, takže A and not B or C je ekvivalentní zápisu (A and (not B)) or C. Samozřejmě je možné použít závorky pro úpravu priority operátorů.

Booleovské operátory and a or jsou takzvané operátory se zkráceným vyhodnocováním: jejich argumenty jsou vyhodnocovány zleva doprava a vyhodnocování se zastaví, když je určen výsledek. Tj. jestliže A a C jsou pravdivé, ale B je nepravdivý, výraz A and B and C nevyhodnotí výraz C.

Je možné přiřadit výsledek porovnání nebo Booleovského výrazu nějaké proměnné. Například:

>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'

V Pythonu, narozdíl od C, se nemůže vyskytovat přiřazení uvnitř výrazu. Programátorům v C to může vadit, ale Python se tímto vyhýbá mnoha chybám programátora, ke kterým dojde zapsáním = ve výrazu namísto ==.

 
5.6 Porovnávání sekvencí a dalších typů

Sekvenční objekty mohou být porovnány s dalšími objekty se stejným typem. Porovnání se děje lexikograficky: nejprve jsou porovnány dva první prvky, jestliže se liší, pak se na základě jejich porovnání určí výsledek porovnání celých sekvencí, jestliže jsou stejné, dojde k porovnání dalších dvou prvků atd. dokud nedojde k vyčerpání sekvence. Jetliže některé dva prvky jsou rovněž sekvenční typy dojde k opětovnému lexikografickému porovnání. Jestliže se prvky obou sekvencí rovnají, rovnají se i obě sekvence. Jestliže jedna sekvence je delší než druhé a ve stejně dlouhé části se shodují, pak ta kratší je vyhodnocena jako menší (ve smyslu operátorů porovnání). Lexikografické porovnávání řetězců používá pro jednotlivé znaky ASCII kódování. Některé příklady porovnání jednotlivých sekvencí stejného typu:

(1, 2, 3)              < (1, 2, 4)
[1, 2, 3]              < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4)           < (1, 2, 4)
(1, 2)                 < (1, 2, -1)
(1, 2, 3)             == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

Porovnávání objektů různých typů je rovněž možné. Typy jsou porovnávány za použití jejich jmen. Takže seznam je vždy meněí než řetězec, řetězec je vždy menší než tuple apod. Různé typy čísel jsou vždy porovnávány s ohledem na jejich číselnou hodnotu, 0 je tedy totéž, co 0.0 atd. 5.1



Footnotes

... atd.5.1
Pravidla pro porovnávání různých typů se mohou v budoucnu změnit, vždy ale bude zaručen jednoznačný výsledek.
See About this document... for information on suggesting changes.