Uživatelské nástroje

Nástroje pro tento web


prog:list

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.

1)
konkrétně řetězec se skládá z jednotlivých znaků
2)
Datové typy s proměnným počtem prvků někdy nazýváme kontejnery nebo také kolekce.
3)
nebo voláním nějaké funkce či metody s návratovou hodnotou tohoto typu
4)
prvky číslujeme od 0 vzestupně, záporná čísla slouží k číslování od konce - první prvek má index 0, poslední -1
5)
Připomínáme, že v případě nenalezení prvku je vyvolána výjimka! Před použitím je tedy vhodné ověřit např. pomocí operátoru in, že hledaný prvek se v seznamu nachází.
6)
totéž platí i pro operátor testu přítomnosti in
prog/list.txt · Poslední úprava: 24.03.2018 16:22 autor: Ivana Stefanová