Hlavní navigace

Létající cirkus (2)

11. 1. 2002
Doba čtení: 10 minut

Sdílet

V dnešním dílu seriálu věnovaného jazyku Python si přiblížíme typy tuple, seznam a asociativní pole, povíme se více o řetězcích, probereme slice konstrukce a na závěr si ukážeme konstrukce pro řízení toku programu.

Sekvenční typy

Mezi tyto typy patří typ tuple a seznam. Jde o konečnou množinu prvků, které jsou indexovány od 0 do n-1, kde n je počet prvků sekvence. Dělí se dále na immutable (neměnné) sekvence a mutable (proměnné) sekvence. U neměnných sekvencí nelze po vytvoření objekt tohoto typu již změnit, lze pouze vytvořit nový z již existujících objektů, kdežto proměnné sekvence lze měnit i po jejich vytvoření.

Tuple

Je prvním ze sekvenčních typů. Patří mezi immutable sekvence, po jeho vytvoření nelze jeho prvky již změnit.

>>> t1 = (1, 2, 3, 4)                          # (1)
>>> t2 = 1, 2, 3, 4                            # (2)
>>> t3 = (1, 'jedna', 'one')                   # (3)
>>> t4 = ()                                    # (4)
>>> t5 = (2, )                                 # (5)
>>> tuples = (t1, t2, t3)                      # (6)

Na předchozích příkladech vidíme několik ukázek zápisu tuple. Jednotlivé prvky tuple se zapisují oddělené čárkou (1), přičemž uzávorkování není povinné (2). Všechny složené typy samozřejmě mohou obsahovat prvky různých typů (3). Prázdný tuple získáme zapsáním dvou kulatých závorek (4) – toto je také samozřejmě jediný případ, kdy není možné vynechat závorky kolem prvků. Na řádku (5) je tuple o jediném prvku. Nesmíme ale zapomenout, že za tímto prvkem je čárka! Interpretr takto rozlišuje mezi tuple a obyčejným výrazem uvedeným v závorce. Samozřejmostí je, že do jednoho tuple může být vložen jiný (6).

Seznam (list)

Je obdobou typu tuple. Jediným rozdílem je, že seznam je sekvence proměnná, tj. jednotlivé prvky seznamu můžeme měnit.

>>> s1 = [1, 2, 3, 4]                          # (1)
>>> s2 = []                                    # (2)
>>> s3 = [2]                                   # (3)

Seznam se zapisuje podobně jako tuple (1), pouze s tím rozdílem, že namísto kulatých závorek píšeme závorky hranaté a nelze je vynechat (při jejich vynechání dostaneme typ tuple, což nechceme). Seznam o jednom prvku dostaneme zápisem hodnoty mezi dvě hranaté závorky, přičemž nemusíme uvádět čárku za prvkem (3). Prázdný seznam získáme podobně jako prázdný tuple zápisem [] (2).

Každý objekt typu seznam má i několik metod (append, count, extend, index, insert, pop, remove, reverse, sort). Pro bližší informace vás odkážu do dokumentace Pythonu, zde uvedu jen malou ukázku použití:

>>> jmena = ['Zbysek', 'Ctirad', 'Emil', 'Adolf']
>>> jmena.insert(0, 'Jenda')
>>> jmena.extend(['Blaza', 'Katka'])
>>> jmena
['Zbysek', 'Jenda', 'Ctirad', 'Emil', 'Adolf', 'Blaza', 'Katka']
>>> jmena.sort()
['Adolf', 'Blaza', 'Ctirad', 'Emil', 'Jenda', 'Katka', 'Zbysek']

Přístup k prvkům sekvenčních objektů

>>> seznam = [1, 2, 3, 4, 5]                   # (1)
>>> seznam[0]                                  # (2)
1
>>> seznam[3] = 5                              # (3)
>>> seznam                                     # (4)
[1, 2, 3, 5, 5]
>>> seznam[0] = seznam                         # (5)
>>> seznam                                     # (6)
[[...], 2, 3, 5, 5]
>>> seznam[0][1]                               # (7)
2

K prvkům objektů typu tuple i seznam se přistupuje stejně, pomocí indexů zapsaných do hranatých závorek za proměnnou (2). U seznamu můžeme přiřadit prvku na určitém indexu novou hodnotu podobně jako na řádku (3). Prvkem seznamu může být opět seznam. Dokonce lze vložit seznam i do sebe samého, Python pak zobrazí místo nekonečné posloupnosti znaky ‚…‘. Seznam ale pořád pracuje tak, jak má, a proto výraz na řádku (7) má hodnotu 2 (nultý prvek ze seznamu ‚seznam‘ je opět tentýž seznam, z něho prvním prvkem je dvojka). Je-li index mimo platný rozsah, dojde k výjimce.

>>> seznam = [1, 2, 3, 4, 5]                   # (1)
>>> seznam[-1]                                 # (2)
5
>>> seznam[-5]                                 # (3)
1
>>> seznam[-6]                                 # (4)

Zajímavé ale je to, co se stane při použití záporného indexu. Nedojde k výjimce, ale prvky se začnou indexovat „odzadu“, takže poslední prvek má index –1 atd. Překročíme-li rozsah pole, opět dojde k výjimce (řádek (4)).

Slice konstrukce

Další velice užitečnou konstrukcí je slice, která umožňuje jakýkoli sekvenční typ doslova „rozřezat“ na kousky. Slice podobně jako indexy používá hranaté závorky, v nichž jsou ale uvedena dvě celá čísla oddělená dvojtečkami.

>>> seznam = (1, 2, 3, 4, 5)                   # (1)
>>> seznam[3:5]                                # (2)
(4, 5)
>>> seznam[0:4]                                # (3)
(1, 2, 3, 4)
>>> seznam[0:50]                               # (4)
(1, 2, 3, 4, 5)
>>> seznam[:3]                                 # (5)
(1, 2, 3)
>>> seznam[1:]                                 # (6)
(2, 3, 4, 5)
>>> seznam[:]                                  # (7)
(1, 2, 3, 4, 5)
>>> seznam[-1:]                                # (8)
(5,)

První z čísel znamená dolní hranici pro výběr prvků, druhé je horní hranice, přičemž není chybou, leží-li tato čísla mimo rozsah platných indexů (4). Je-li první číslo vynecháno, nahradí se číslem 0, je-li vynecháno druhé, dosadí se za něj hodnota proměnné sys.maxint (defacto maximální velikost integeru) (5)(6)(7). I zde je možné používat záporná čísla podobně jako u indexů (8). Nejsou-li v rozsahu žádné prvky, vrátí slice konstrukce prázdnou sekvenci. Zde je třeba zmínit, že slice vždy vrací novou sekvenci toho typu, na nějž se aplikuje. I když se slice konstrukcí vybere jediný prvek, vždy se jedná o sekvenci! Podobně při použití slice s vynechaným dolním i horním indexem dojde k vytvoření nové sekvence!

Řetězce

V předchozím dílu jsem vám záměrně zatajil jednu důležitou vlastnost řetězců, ty totiž patří také mezi sekvenční immutable typy, z čehož vyplývá, že na ně lze aplikovat stejné funkce a konstrukce jako na tuple a seznamy. Totéž platí samozřejmě i pro unicodové řetězce.

>>> str = 'linux: try or die'
>>> str[:5]
'linux'
>>> len(str)
17
>>> str[5]
':'

Sekvence stejného typu lze spojovat pomocí operátoru +. Vynásobíme-li sekvenci celým číslem n, získáme sekvenci, v níž se původní sekvence opakuje n-krát.

>>> x = (1, 2)
>>> y = (3, 4)
>>> (x + y) * 3
(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4)

Objekt typu tuple lze zkonvertovat na seznam a naopak. Děje se tak pomocí vestavěných funkcí list() a tuple(). Podobně i řetězec lze zkonvertovat na tuple nebo seznam.

>>> t = (1, 2, 3, 4)
>>> list(t)
[1, 2, 3, 4]
>>> tuple(_)
(1, 2, 3, 4)
>>> list('linux')
['l', 'i', 'n', 'u', 'x']

Mapované typy

Další skupinou typů jsou mapované typy. Jde o konečnou množinu prvků indexovaných libovolnou množinou indexů. Mezi mapované typy patří asociativní pole a jako mapovaný typ je implementován např. i přístup k databázím dbm apod.

Asociativní pole (dictionaries)

Jde o pole, kde se pro indexování prvků používá libovolných objektů. Jediné omezení na tyto objekty (klíče) je, aby z nich bylo možno určit jejich hash. To lze ale pouze pro objekty neměnné (immutable sekvence, řetězce, čísla a další, viz dokumentace k Pythonu).

>>> znamky = {'Voprsalek': 3, 'Macha': 5,      # (1)
... 'Novak': 1}                                # (2)
>>> znamky['Voprsalek']                        # (3)
3
>>> znamky['Macha'] = 4                        # (4)
>>> znamky['Svec'] = 2                         # (5)
>>> znamky                                     # (6)
{'Svec': 2, 'Macha': 4, 'Voprsalek': 3, 'Novak': 1}

Na řádcích (1)(2) jsme vytvořili nové asociativní pole. To se zapisuje jako páry klíč: hodnota oddělené čárkou mezi složenými závorkami. Pro přístup k hodnotám se používá klíč zapsaný ve hranatých závorkách za proměnnou. Přistujeme-li k indexu, který není v asociativním poli, dojde opět k výjimce. Vytvoření nového páru klíč: hodnota pomocí přiřazení je samozřejmostí (5).

Také objekt typu asociativní pole má několik metod (clear, copy, get, has_key, items, keys, setdefault, update, values). Zde vás opět odkážu na dokumentaci.

Konstrukce pro řízení toku programu

Žádný programovací jazyk se bez nich neobejde. A ani Python není výjimkou. Tento programovací jazyk používá pouhé tři konstrukce, kterými lze ovlivnit, co se bude v programu dít. Jsou to if, for a while.

Konstrukce if

Je základní konstrukce pro řízení toku programu. Předpokládám, že tento článek čtou lidé, kteří již mají nějaké zkušenosti s programováním, a proto pouze popíši, jakým způsobem se používá.

>>> D = 2                                      # (1)
>>> if D > 0:                                  # (2)
...     print 'rovnice ma 2 reseni'            # (3)
... elif D == 0:                               # (4)
...     print 'rovnice ma 1 reseni'            # (5)
... else:                                      # (6)
...     print 'rovnice nema reseni'            # (7)
...                                            # (8)
rovnice ma 2 reseni

Toto je klasická školní ukázka, jak určit počet kořenů kvadratické rovnice v oboru reálných čísel. Všimněte si, že v Pythonu se nepoužívá žádný druh uvození bloků programu jako třeba v C/C++ nebo Pascalu. Jednotlivé bloky jsou určeny odsazením od kraje. Nezáleží na tom, kolik odsadíte a jestli použijete mezery nebo tabulátory, rozhodující je, aby odsazení bylo v celém bloku stejné. Pozor při kopírování řádků kódu např. z webového browseru do editoru. V editoru vypadá vše, jak má, kód funguje, ale doplníte-li nějaký vlastní kód, interpretr si stěžuje na chybu v syntaxi. Háček je v tom, že při kopírování se text odsadil jen mezerami, a když jste doplnili text v editoru a odsadili jste ho tabulátorem, začal si Python stěžovat. Odsazení od kraje je geniální věc, nedochází k žádným nejednoznačným konstrukcím, kdy se např. neví, k jakému if daný else patří. A tento způsob formátování také rozhodně prospěje vzhledu celého zdrojového kódu programu. V Pythonu platí jedna zásada, končí-li jeden řádek na dvojtečku, následující musí být odsazen. Chceme-li, aby bylo tělo bloku prázdné, nelze ho vynechat, musíme místo bloku uvést konstrukci pass, jde o prázdnou konstrukci, nedělá vůbec žádnou akci, je zde pouze pro uspokojení parseru. U konstrukcí if nemá praktický význam, hodí se ale u konstrukcí try … except, když chceme, aby byla výjimka odchycena, ale neprovedla se žádná akce. Doplňme, že stejně jako bloky v konstrukci if se odsazují i bloky v cyklech for a while, při definicích funkcí a tříd, u konstrukce try … except apod.

Konstrukce for:

Tato konstrukce se liší od svého jmenovce v jazyce C. V Pythonu cyklus for prochází objekt sekvenčního typu a jednotlivé prvky dosazuje do řídící proměnné. Stejně jako v jiných jazycích, i zde můžeme použít konstrukce break a continue. Zvláštností Pythonu je, že i cyklus for může mít část else, která se vykoná po zpracování všech prvků sekvence. Je-li ovšem cyklus přerušen pomocí break nebo v jeho tělo dojde k výjimce, část else se neprovede. Chceme-li, aby cyklus for iteroval přes množinu celých čísel (podobně jako třeba v Pascalu), musíme tuto množinu nejprve vytvořit jako sekvenci pomocí vestavěné funkce range().

>>> soucet = 0
>>> for cislo in range(50):
...     soucet += cislo
... else:
...     print 'soucet cisel od 0 do 49 je', soucet
...
soucet cisel od 0 do 49 je 1225

Funkce range() vrací seznam, naplněný čísly. Je-li jí předáno jako argument jedno číslo n, obsahuje seznam čísla 0 až n-1. Jsou-li jí předána čísla dvě, řekněme m a n, seznam bude naplněn čísly m až n-1. Jsou-li jí předána tři čísla x, y a z, vrátí čísla od x až po y-1 s krokem z. Seznam je v paměti alokován najednou, proto se u velkých rozsahů, kde by bylo zbytečné uchovávat jednotlivé prvky v paměti, používá funkce xrange() se stejným rozhraním.

>>> range(9)
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> range(2, 9)
[2, 3, 4, 5, 6, 7, 8]
>>> range(2, 9, 2)
[2, 4, 6, 8]

Je-li jako sekvence použita jakákoli proměnná sekvence, je třeba dát pozor, zda se s jednotlivými prvky a s jejich pořadím v těle cyklu nemanipuluje, pak by totiž jeden prvek mohl být zpracován dvakrát, zatímco jiný by byl vynechán. V tomto případě je vhodné vytvořit dočasnou kopii seznamu pomocí slice konstrukce.

>>> seznam = [1, 2, 3]
>>> for item in seznam:
...     print 'pridavam', item
...     seznam.append(item)
... else:
...     print 'seznam byl zdvojen'
...

Předchozí příklad vede k nekonečnému cyklu. Nápravou je vytvoření dočasné kopie seznamu pomocí slice. Nyní vše již pracuje tak, jak má:

>>> seznam = [1, 2, 3]
>>> for item in seznam[:]:
...     print 'pridavam', item
...     seznam.append(item)
... else:
...     print 'seznam byl zdvojen'
...

Konstrukce while

Je obdoba cyklu while v C. Její tělo je vykonáváno, dokud je výraz – podmínka vyhodnocen jako true. I zde je možné použít nepovinou část else, která se vykoná při ukončení cyklu. Podobně jako u cyklu for lze použít konstrukce break a continue pro ukončení cyklu, respektive pro pokračování další iterací. Při ukončení cyklu pomocí break se taká samozřejmě neprovede část else.

root_podpora

>>> cislo = 0
>>> soucet = 0
>>> while cislo < 50:
...     soucet += cislo
...     cislo += 1
... else:
...     print 'soucet cisel od 0 do 49 je', soucet
...
soucet cisel od 0 do 49 je 1225

Vstup od uživatele

Abyste mohli napsat jednoduchý interaktivní program, potřebujete ještě vědět, jakým způsobem se může program zeptat uživatele na data. Úplně nejzákladnější funkcí je funkce raw_input(), která přejímá jeden nepovinný argument. Ta se zeptá uživatele na data, která pak vrátí jako řetězec. Přitom použije argument jako výzvu. Existuje ještě funkce input(), která zadaný řetězec nejprve vyhodnotí jako výraz jazyka Python a až poté vrátí hodnotu tohoto příkazu. Bližší informace najdete v dokumentaci.

Příště

Ve třetím dílu našeho povídání se řekneme něco o tom, jak Python vyhodnocuje logické výrazy, a podíváme se, co vše nám nabízí pro definování a používání funkcí.

Byl pro vás článek přínosný?