Obsah
Seznamy - pokračování
Možnosti seznamů jsme dosud rozhodně nevyčerpali, bez řady níže popsaných věcí bychom se v praxi jen těžko obešli.
Úpravy seznamů
Dosavadní výklad týkající se seznamů byl do značné míry založen na analogiích s textovými řetězci. Nyní se zaměříme na možnosti principiálně nové - přímé úpravy seznamů.
Pomocí indexovacího operátoru můžeme kromě čtení také měnit konkrétní prvky seznamu nebo dokonce celé řezy. Takové možnosti u řetězců nemáme.
jmena = ["Jan", "Petra", "Jakub", "Marie"] # práce s jedním prvkem seznamu jmena[2] # => 'Jakub' (to není nic nového) jmena[2] = "Pavel" # změna prvku s indexem 2 (přiřazení nové hodnoty) jmena # => ['Jan', 'Petra', 'Pavel', 'Marie'] # práce s řezem (podseznamem) jmena[1:3] # => ['Petra', 'Pavel'] (opět známá záležitost) jmena[1:3] = ["Tereza", "David"] # nahradíme řez jiným seznamem jmena # => ['Jan', 'Tereza', 'David', 'Marie'] jmena[0:2] = ["Karel", "Aneta", "Jana", "Josef"] # nahrazení řezu seznamem jiné délky (změna počtu prvků seznamu) jmena # => ['Karel', 'Aneta', 'Jana', 'Josef', 'David', 'Marie'] jmena[2:4] = [] # nahrazení řezu prázdným seznamem (tj. vymazání) jmena # => ['Karel', 'Aneta', 'David', 'Marie']
Seznamy lze měnit také pomocí některých metod.
cisla = [14, 2, -5, 8] # přidání prvku na konec seznamu cisla.append(11) cisla # => [14, 2, -5, 8, 11] # připojení seznamu na konec cisla.extend([0, -4]) cisla # => [14, 2, -5, 8, 11, 0, -4] cisla += [2, 3] # jiný způsob zápisu pro stejnou činnost cisla # => [14, 2, -5, 8, 11, 0, -4, 2, 3] # odstranění prvku na konci seznamu cisla.pop() # => 3 (je vrácena hodnota mazaného prvku) cisla # => [14, 2, -5, 8, 11, 0, -4, 2] # odstranění prvku na určené pozici (dané indexem) cisla.pop(3) # => 8 (opět vrací hodnotu prvku) cisla # => [14, 2, -5, 11, 0, -4, 2] # vložení nového prvku na určenou pozici cisla.insert(2, 10) # na pozici s indexem 2 je vložen nový prvek cisla # => [14, 2, 10, -5, 11, 0, -4, 2] # vymazání prvku s určenou hodnotou cisla.remove(2) # první prvek s hodnotou 2 je vymazán cisla # => [14, 10, -5, 11, 0, -4, 2] cisla.remove(20) # => chyba ValueError, protože prvek 20 neexistuje
Minimum, maximum a součet
Při práci se seznamy, jejichž prvky lze porovnávat1) (typicky pole čísel nebo řetězců) můžeme pomocí funkcí min
a max
určit hodnotu nejmenšího a největšího prvku seznamu.
min([12.1, -3.1, 5.04, 6, 0.215]) # => -3.1 max(["haf", "kokodák", "bůů", "mňau"]) # => 'mňau'
O problematice řazení řetězců jsme již hovořili. Není překvapivé, že použití funkcí min
a max
na prázdný seznam skončí chybou (ValueError
), stejně tak chybou (TypeError
) skončí pokus o porovnání prvků nekompatibilních typů, např. pro seznam s prvky typu int
a zároveň str
.
Pro seznam složený pouze z číselných prvků můžeme snadno určit součet všech jeho prvků funkcí sum
, součet prázdného seznamu je samozřejmě 0
.
sum([1, 2, 3, 4, 5, 6]) # => 21 sum([0.25, -1.08, 1, 0, 0.15]) # => 0.31999999999999995
Porovnávání seznamů
Seznamy je obecně možné také porovnávat. Postupuje se přitom dle podobného principu jako u řetězců - porovnávají se postupně od začátku odpovídající prvky než se narazí na rozdíl nebo je některý seznam vyčerpán (tzv. lexikografického uspořádání). Z toho mj. plyne, že odpovídající prvky musí být porovnatelné.
[1, 2, 3] < [1, 3, 5] # => True (rozdíl ve 2. prvku) [1, "aa"] < [1, "ab"] # => True (odpovídající prvky stejných typů) [] < [-1, 0, 1] # => True (prázdný seznam je vždy menší) [1, 2] < [1, 2, 3] # => True (vyčerpání seznamu vlevo) [1, 2, 3] < [2] # => True (rozdíl již v 1. prvku) [1, 2, 3] < [2, "A"] # => True (na porovnání 2. prvků vůbec nedojde) [1, 2, 3] < [1, "A"] # chyba 'TypeError' při porovnání 2. prvků
Generátorová notace
V praxi často potřebujeme vytvořit seznam s prvky, které jsou vytvořeny podle nějakého pravidla (např. druhé mocniny prvních pěti přirozených čísel). To bychom s již nabytými znalostmi pomocí jednoduchého cyklu dokázali, ale Python nám pomocí tzv. generátorové notace (anglicky list comprehension) dává elegantní možnost zkráceného zápisu. Místo
ctverce = [] for i in range(1, 6): ctverce.append(i * i)
tak můžeme zapsat
ctverce = [i * i for i in range(1, 6)]
se stejným výsledkem [1, 4, 9, 16, 25]
. Zápis je kratší a přitom dobře srozumitelný. Skládá se z nějakého cyklu (v našem případě for i in range(1, 6)
), před nímž stojí požadovaná operace s řídicí proměnnou cyklu (v našem případě i * i
) a nepovinně může být zakončen ještě nějakou podmínkou (jak uvidíme v následujících případech).
# přirozená čísla menší než 6 [i for i in range(1, 6)] # => [1, 2, 3, 4, 5] # lichá čísla menší než 10 [i for i in range(1, 10, 2)] # => [1, 3, 5, 7, 9] # čísla od 0 po 1/10 menší než 0.5 [0.1 * i for i in range(5)] # => [0.0, 0.1, 0.2, 0.30000000000000004, 0.4] # vzdálenosti od 0 do 0.5 m jako řetězce na 2 des. místa s jednotkou ["{:.2f} m".format(0.1 * i) for i in range(5)] # => ['0.00 m', '0.10 m', '0.20 m', '0.30 m', '0.40 m'] # pomocí podmínky za 'if' lze hodnoty omezit # přirozená čísla, která jsou dělitelná 3 nebo 5 [i for i in range(1, 30) if i % 3 == 0 or i % 5 == 0] # => [3, 5, 6, 9, 10, 12, 15, 18, 20, 21, 24, 25, 27] # kódy řídicích znaků (ASCII 0 až 31), které jsou pokládány za "bílé" [i for i in range(32) if chr(i).isspace()] # => [9, 10, 11, 12, 13, 28, 29, 30, 31]
V generátorové notaci může být použit i více než jeden cyklus (což odpovídá použití vnořených cyklů).
# dvojciferná čísla, na pozici desítek 3 nebo 5, jednotky 1, 3 nebo 7 [10 * i + j for i in [3, 5] for j in [1, 3, 7]] # => [31, 33, 37, 51, 53, 57] # seznam všech 64 polí šachovnice [i + str(j) for i in "abcdefgh" for j in range(1, 9)] # => ['a1', 'a2', 'a3', 'a4', ..., 'h6', 'h7', 'h8']
Možná si vzpomenete, že jsme v jedné úloze hledali trojciferná čísla taková, že součet třetích mocnin jejich číslic je roven tomuto číslu. Pomocí generátorové notace lze i tento seznam přímo vytvořit.
[i for i in range(100, 1000) if (i//100)**3+(i%100//10)**3+(i%10)**3 == i] # => [153, 370, 371, 407]
Proměnné použité v rámci generátorové notace v cyklech uvnitř hranatých závorek se chovají podobně jako lokální proměnné ve funkcích, tj. nijak nemění hodnoty případných jiných proměnných stejného jména mimo hranaté závorky.