Seznamy
Dosud jsme se seznámili s jednoduchými datovými typy jako int
nebo bool
a naposledy i s řetězci jako prvním zástupcem složených datových typů - tedy takových, které mohou obsahovat více nějakých jednotlivých prvků1). Snad nejuniverzálnějším datovým typem jazyka Python je seznam (anglicky list), bez něhož si prakticky nelze představit implementaci ohromného množství zásadních algoritmů v programátorské praxi.
Seznam se skládá z prvků, které mohou být libovolného datového typu (int
, float
, str
atd.), přičemž typ jednotlivých prvků ani nemusí být shodný. Počet prvků není pevně určen, můžeme mít prázdný seznam, seznam s 1, 2, 5, 1089, … prvky2). Pokud je typ jednotlivých prvků shodný, často se v informatice označuje jako pole (v tomto smyslu se o řetězcích často hovoří jako o polích znaků), použití seznamu v roli pole je poměrně typické.
Inicializace seznamu
Proměnnou typu list
(tj. seznam) vytvoříme pomocí výčtu jeho prvků3). Jednotlivé prvky zapisujeme tak, jak jsme zvyklí, celou inicializaci uzavřeme do hranatých závorek a jednotlivé prvky oddělujeme čárkou.
adresa = ["Palackého nábřeží", 28, 39201, "Soběslav", True] vyska_zaku = [1.56, 1.61, 1.45, 1.38, 1.50, 1.63, 1.48, 1.58] neprospeli = []
Seznam adresa
má 5 prvků (2 řetězce, 2 celá čísla a 1 logickou hodnotu), vyska_zaku
je pole 8 hodnot typu float
vyjadřující výšku několika žáků v metrech a konečně seznam neprospeli
je prázdný.
Základní operace
Protože řetězce a seznamy vycházejí ze společné filozofie, je řada operací s nimi shodná nebo velmi podobná (což na druhé straně přispívá ke srozumitelnosti a lepší použitelnosti jazyka).
Nepřekvapí nás tedy zjištění počtu prvků seznamu, sloučení seznamů pomocí sčítání, opakování pomocí násobení přirozeným číslem, přístup k jednotlivým prvkům pomocí indexování4) a analogické je i vytvoření podseznamu (řezu) pomocí dvojice (případně trojice) indexů.
len(vyska_zaku) # => 8 (počet prvků) len(neprospeli) # => 0 [1, 2, 3] + ["a", "b"] # => [1, 2, 3, "a", "b"] (sčítání) ["a", "b"] * 2 # => ["a", "b", "a", "b"] (násobení) 28 in adresa # => True (test přítomnosti prvku) "Palackého" not in adresa # => True (tento řetězec není prvkem) adresa[0] # => "Palackého nábřeží" (indexování) vyska_zaku[-2] # => 1.48 (indexování od konce) vyska_zaku[1:4] # => [1.61, 1.45, 1.38] (podseznam) adresa[-2:] # => ['Soběslav', True] vyska_zaku[::3] # => [1.56, 1.38, 1.48] (s indexy 0, 3, 6)
Jistě nás nepřekvapí, že seznamy jsou objekty a jsou s nimi spojeny nějaké metody. Podobně jako u řetězců máme metodu list.count(vzor)
(včetně variant s omezeným rozsahem hledání list.count(vzor, od)
a list.count(vzor, od, do)
) pro zjištění počtu výskytů vzoru v seznamu.
vyska_zaku.count(1.38) # => 1 (nalezen 1 prvek) vyska_zaku.count(1.60) # => 0 (nenalezeno)
Vyhledávání pozice prvku v seznamu se analogicky k řetězcům provede pomocí metody list.index(vzor)
(máme opět i varianty s omezením rozsahu hledání)5). Seznamy však nemají metody rindex, find ani rfind. Rozdíl je také v tom, že metoda index
vyhledává vždy pouze jeden prvek (nikoliv nějaký podseznam)6).
vyska_zaku.index(1.38) # => 3 (první výskyt na indexu 3) vyska_zaku.index(1.60) # => výjimka ValueError (nenalezeno)
Po předání seznamu jako parametru funkci print
se vytiskne znaková reprezentace v hranatých závorkách s prvky oddělenými čárkou.
test = ["slovo", -5, 16.800, False] print(test) # => ['slovo', -5, 16.8, False]
V logickém kontextu má prázdný seznam hodnotu False
, libovolný neprázdný seznam hodnotu True
(což opět v analogii s řetězci není nic překvapivého).
bool(test) # => True bool(neprospeli) # => False
Při procházení seznamu (tj. postupnému přístupu k jeho jednotlivým prvkům) můžeme využít cyklus for
, prvky se přiřazují přímo do řídicí proměnné cyklu.
for i in test: print("prvek <", i, "> je typu", type(i))
vytiskne následující čtyři řádky:
prvek < slovo > je typu <class 'str'> prvek < -5 > je typu <class 'int'> prvek < 16.8 > je typu <class 'float'> prvek < False > je typu <class 'bool'>
Příklad použití
Na krátkém programu si ukážeme použití seznamů - i vzhledem k několikrát zmiňované podobnosti s řetězci by kód měl být dobře pochopitelný. Pro zadaný seznam žáků provedeme vstup známky pro každého z nich a následně výsledky zpracujeme.
- znamky.py
# při inicializaci delších seznamů můžeme za čárkou # pokračovat na dalším řádku zaci = ["Karel Borovský", "Jindřich Baar", "Magdaléna Rittigová", "Jakub Ryba", "Johann Goethe", "František Šalda"] # zadávání známek # celé číslo 1 až 5, případně prázdný vstup print("Vstup dat") znamky = [] # vytvoříme seznam známek indexovaný stejně jako žáci for zak in zaci: znamka = input(" zadejte známku žáka " + zak + ": ") if znamka: # převod na celé číslo, pokud bylo zadáno znamka = int(znamka) # převod známky na seznam a sloučení seznamů znamky += [znamka] print() # nyní je možné seznamy dále zpracovávat # statistika známek print("Statistika") for i in range(1, 6): print(" ", i, ":", znamky.count(i)) print(" - :", znamky.count("")) # bez klasifikace print() # průměrná známka soucet = 0 pocet = 0 # akumulace dat for i in znamky: if type(i) == int: # pouze platné známky soucet += i pocet += 1 # vyhodnocení if pocet: print("Průměr: {:.2f}".format(soucet / pocet)) print() # výpis žáků s jedničkou jednicek = znamky.count(1) if jednicek > 0: print("Žáci s klasifikací '1'") od = 0 # hledá od indexu 0 for i in range(jednicek): # nalezení indexu jedničky, tj. i žáka index = znamky.index(1, od) print(" č.", i + 1, ":", zaci[index]) od = index + 1 # hledání další jedničky
Seznam známek vytváříme způsobem, který zajišťuje shodnou velikost se seznamem žáků i odpovídající pořadí. Při rozšíření počtu žáků zůstává zbytek programu beze změny.
Díky použití seznamu je možné oddělit zpracování od vstupu. Řadu úloh přitom průběžně se zadáváním dat (tedy bez jejich uložení) ani řešit nelze, např. určení počtu prvků, které jsou nad a pod průměrem. Všimněte si také, že nikde neurčujeme předem nějaký pevný počet prvků seznamu.
Za poznámku snad stojí připomenutí, že i jednotlivou známku před přidáním do seznamu je nutné převézt na (jednoprvkový) seznam (operace součtu položek list
a int
není definována). Zkrácené přiřazení ve formě +=
či *=
lze použít i v případě seznamu na levé straně příkazu.
Konverze mezi seznamy a řetězci
Seznamy není možné přímo převádět na jiné jednoduché datové typy a naopak, např. pokusy cisla = float([5.129, -0.15])
nebo stav = list(True)
skončí chybou TypeError
. Pozorný čtenář ale již zaznamenal jeden případ, kdy toto pravidlo neplatí, a sice použití seznamu v logickém kontextu (neprazdny = bool([0, 1, 2, 3])
je zcela legální příkaz).
Jiná je situace při srovnání seznamů a řetězců, kde jsme zaznamenali řadu podobností. Z hlediska Pythonu se v obou případech jedná o iterables, tj. objekty, které je možné procházet prvek po prvku. Při přímé typové konverzi seznamu na řetězec je výsledek shodný jako u funkce print
(tj. tisknutelná podoba seznamu), při opačném postupu (převodu řetězce na seznam) je vytvořen seznam, jehož prvky tvoří jednotlivé znaky řetězce.
str([12.8, True, "Praha"]) # => "[12.8, True, 'Praha']" list("Praha") # => ['P', 'r', 'a', 'h', 'a']
Tím však nejsou vyčerpány všechny možnosti vzájemného převodu. Objekty typu řetězec mají pro tyto účely další specializované metody.
# rozdělení řetězce na úseky na základě určeného oddělovače # str.split() # - bez parametru je dělení provedeno na všech bílých znacích "Běží liška k Táboru".split() # => ['Běží', 'liška', 'k', 'Táboru'] # oddělovače jsou odstraněny "1 2\t3\n4".split() # => ['1', '2', '3', '4'] "jedna \n\n dva \n".split() # => ['jedna', 'dva'] # opakovaný a závěrečný oddělovač # str.split(separator) # - s určením znaku či podřetězce, na kterém je prováděno dělení "12.5,8.48,-6".split(",") # => ['12.5', '8.48', '-6'] "12,5, 8,48, -6".split(", ") # => ['12,5', '8,48', '-6'] # des. ČÁRKA v čísle je zachována # str.split(separator, pocet) # - s určením oddělovače a maximálního počtu dělení "1-2-3-4-5-6".split("-", 2) # => ['1', '2', '3-4-5-6'] "1-2-3-4-5-6".split("-", 0) # => ['1-2-3-4-5-6'] "1-2-3-4-5-6".split("-", 10) # => ['1', '2', '3', '4', '5', '6'] # rozdělení řetezce po řádcích # str.splitlines() # - dělení na ukončeních řádku ('\n', '\r', '\r\n', '\v' aj.) "1\n2\r3\n4".splitlines() # => ['1', '2', '3', '4'] # str.splitlines(True) # - oddělovače na konci řádku jsou zachovány "1\n2\r3\n4".splitlines(True) # => ['1\n', '2\r', '3\n', '4'] # sloučení seznamu řetězců na základě spojovníku # str.join(zdroj) # - spojovník je určen samotným řetězcem "".join(['B', 'r', 'n', 'o']) # => 'Brno' # opačná operace k list("Brno") "--".join(['B', 'r', 'n', 'o']) # => 'B--r--n--o'
Popsané metody nám dávají řadu možností, jak rozdělit řetězec na seznam jeho podřetězců i na zpětné sloučení do jednoho řetězce. Vzpomeňte si na úlohu počítání počtu slov v zadaném textu. S novými poznatky se nabízí využití metody split
, která nám poskytne nejen počet slov (jako velikost výsledného seznamu), ale přímo jednotlivá slova, která můžeme dále zpracovávat.
in
, že hledaný prvek se v seznamu nachází.in