Janko a Marienka pokračujú v dobrodružnej hre a potrebujú uložiť viacero predmetov do jednej premennej. Pomôžu im zoznamy a n-tice.
Čo sa naučíte
Po skončení kapitoly budete vedieť:
- vytvárať zoznamy v Pythone;
- pridávať nové prvky pomocou
append(); - spájať viacero zoznamov;
- určovať dĺžku zoznamu;
- vyberať časti zoznamu pomocou výrezov (slicing);
- prechádzať zoznamom a meniť poradie jeho prvkov;
- vyhľadávať, filtrovať a transformovať prvky;
- používať funkcie
filter(),map()areduce(); - vytvárať zoznamy pomocou zoznamovej notácie (list comprehension).
Dobrodružstvo v košickom lese
Janko putuje čarovným košickým lesom a zbiera poklady. Namiesto ukladania každého predmetu do samostatnej premennej nosí všetko v jednom batohu. V Pythone tento batoh predstavuje zoznam.
Každá situácia v príbehu predstaví inú operáciu so zoznamom — od pridávania predmetov až po ich filtrovanie a transformáciu.
Janko začína svoje dobrodružstvo s prázdnym batohom.
backpack = []
print(backpack)
Výstup:
[]
Po chvíli nájde meč, jablko a starú mapu. Všetky predmety uloží do jedného zoznamu.
backpack = ["Meč", "Jablko", "Mapa"]
print(backpack)
Výstup:
['Meč', 'Jablko', 'Mapa']
Jeden zoznam môže obsahovať aj rôzne typy údajov. Výbavu môžete napríklad doplniť o počet mincí, informáciu o splnenej úlohe či hmotnosť predmetu.
mixed = ["Meč", 100, True, 5.7]
print(mixed)
Výstup:
['Meč', 100, True, 5.7]
Pridanie predmetu pomocou append()
Jankov batoh už nie je prázdny, no počas cesty doň pribúdajú ďalšie nálezy. Keď nájde štít, pridá ho na koniec existujúceho zoznamu pomocou metódy append().
backpack.append("Štít")
print(backpack)
Výstup:
['Meč', 'Jablko', 'Mapa', 'Štít']
Rovnakým spôsobom si o niečo neskôr odloží aj liečivý elixír.
backpack.append("Elixír")
print(backpack)
Výstup:
['Meč', 'Jablko', 'Mapa', 'Štít', 'Elixír']
Metóda append() vždy pridá jeden nový prvok na koniec zoznamu. Janko však čoskoro potrebuje do batohu pridať viac predmetov naraz.
Spojenie zoznamov pomocou + a extend()
Na ďalšej ceste Janko stretne svoju kamarátku Marienku, ktorá nesie vlastnú výbavu.
marienka = ["Luk", "Šíp"]
Najprv si vytvoria nový spoločný zoznam pomocou operátora +. Ich pôvodné batohy pritom zostanú nezmenené.
combined = backpack + marienka
print(combined)
Výstup:
['Meč', 'Jablko', 'Mapa', 'Štít', 'Elixír', 'Luk', 'Šíp']
Ak chce Janko namiesto toho vložiť Marienkinu výbavu priamo do svojho batoha, použije metódu extend().
backpack.extend(marienka)
print(backpack)
Výstup:
['Meč', 'Jablko', 'Mapa', 'Štít', 'Elixír', 'Luk', 'Šíp']
Na rozdiel od operátora + metóda extend() upravuje pôvodný zoznam. Po spojení výbavy preto Janka zaujíma, koľko predmetov teraz nesie.
Počet prvkov pomocou len()
Batoh je čoraz ťažší. Počet jeho prvkov Janko zistí pomocou funkcie len().
print(len(backpack))
Výstup:
7
Samotný počet mu však nepovie, ktoré predmety má pri sebe. Preto si ich postupne prezrie.
Vyhľadávanie a odstraňovanie prvkov
Operátor in overí, či sa prvok v zozname nachádza. Pozíciu prvku získame
pomocou index() a počet výskytov pomocou count().
print("Mapa" in backpack) # True
print(backpack.index("Mapa")) # 2
print(backpack.count("Mapa")) # 1
Pri odstraňovaní záleží na tom, či poznáme hodnotu alebo pozíciu:
backpack.remove("Jablko") # odstráni prvý prvok s touto hodnotou
last_item = backpack.pop() # odstráni a vráti posledný prvok
first_item = backpack.pop(0)
remove() vyvolá ValueError, ak hodnota v zozname nie je. pop() vyvolá
IndexError, ak požadovaná pozícia neexistuje.
Prechádzanie zoznamom pomocou for a enumerate()
Na prechod všetkými predmetmi použije cyklus for. V každom opakovaní sa do premennej item uloží jeden prvok zoznamu.
for item in backpack:
print(item)
Výstup:
Meč
Jablko
Mapa
Štít
Elixír
Luk
Šíp
Ak chce pri každom predmete poznať aj jeho pozíciu, spojí cyklus s funkciou enumerate().
for index, item in enumerate(backpack):
print(index, item)
Výstup:
0 Meč
1 Jablko
2 Mapa
3 Štít
4 Elixír
5 Luk
6 Šíp
Poradie predmetov nemusí Jankovi vždy vyhovovať. Niekedy sa potrebuje dostať najskôr k tým, ktoré vložil do batoha ako posledné.
Výber časti zoznamu pomocou výrezu
Janko niekedy nepotrebuje celý obsah batoha, ale iba niekoľko predmetov. Časť zoznamu môže vybrať pomocou výrezu (slice).
Základný zápis má tvar:
zoznam[od:po:krok]
Hodnota od určuje index prvého vybraného prvku. Hodnota po označuje hranicu, ktorá sa už do výrezu nezahrnie.
backpack = ["Meč", "Jablko", "Mapa", "Štít", "Elixír", "Luk", "Šíp"]
print(backpack[1:4])
Výstup:
['Jablko', 'Mapa', 'Štít']
Výrez začína prvkom s indexom 1 a končí tesne pred indexom 4. Ak niektorú hranicu vynecháme, Python použije začiatok alebo koniec zoznamu.
print(backpack[:3])
print(backpack[4:])
Výstup:
['Meč', 'Jablko', 'Mapa']
['Elixír', 'Luk', 'Šíp']
Tretia hodnota určuje krok. Napríklad krokom 2 vyberieme každý druhý predmet.
print(backpack[::2])
Výstup:
['Meč', 'Mapa', 'Elixír', 'Šíp']
Použiť možno aj záporné indexy, ktoré počítajú prvky od konca. Index -1 označuje posledný prvok.
print(backpack[-3:])
Výstup:
['Elixír', 'Luk', 'Šíp']
Rezanie vytvára nový zoznam a pôvodný zoznam nemení. Zápis backpack[:] preto vytvorí plytkú kópiu celého zoznamu. Záporný krok, napríklad backpack[::-1], vytvorí zoznam v opačnom poradí; túto možnosť využijeme neskôr pri obracaní batoha.
Obrátenie poradia pomocou reverse() a výrezu
Najnovšie nájdené predmety sú navrchu batoha, preto Janko obráti poradie celého zoznamu metódou reverse().
backpack.reverse()
print(backpack)
Výstup:
['Šíp', 'Luk', 'Elixír', 'Štít', 'Mapa', 'Jablko', 'Meč']
Metóda reverse() mení pôvodný zoznam. Ak Janko potrebuje iba obrátenú
kópiu, môže použiť výrez s krokom -1.
print(backpack[::-1])
Rezanie vytvorí nový zoznam, takže aktuálne poradie v batohu nezmení. Ak však Janko nepotrebuje nový zoznam a chce predmety iba postupne prečítať odzadu, vhodnejšia je funkcia reversed().
Pred ďalšou ukážkou Janko vráti predmety do pôvodného poradia.
backpack.reverse()
Prechádzanie odzadu bez zmeny zoznamu: reversed()
Janko si chce prezrieť obsah batoha od najnovšieho predmetu po najstarší, no tentoraz nechce meniť ich uloženie.
Na to slúži funkcia reversed(), ktorá vytvorí iterátor prechádzajúci prvky odzadu.
for item in reversed(backpack):
print(item)
Výstup:
Šíp
Luk
Elixír
Štít
Mapa
Jablko
Meč
Po skončení cyklu zostáva pôvodný zoznam nezmenený.
print(backpack)
Výstup:
['Meč', 'Jablko', 'Mapa', 'Štít', 'Elixír', 'Luk', 'Šíp']
To je hlavný rozdiel oproti metóde reverse(), ktorá poradie prvkov v zozname zmení priamo. Opačné poradie však nie je jediný spôsob, ako sa dá na obsah batoha pozerať.
Vkladanie prvkov a triedenie zoznamu
Metóda insert() vloží prvok na zadanú pozíciu. Metóda sort() zoradí
pôvodný zoznam, zatiaľ čo sorted() vytvorí nový zoznam.
items = ["Meč", "Luk", "Štít"]
items.insert(1, "Mapa")
items.sort()
print(items)
Ak chcete zachovať pôvodné poradie, použite funkciu sorted():
items = ["Meč", "Luk", "Štít"]
ordered = sorted(items)
Prechádzanie v abecednom poradí: sorted()
Pri kontrole výbavy chce Janko niekedy zobraziť predmety podľa abecedy bez toho, aby zmenil ich skutočné poradie v batohu.
Na to slúži funkcia sorted().
for item in sorted(backpack):
print(item)
Výstup:
Elixír
Jablko
Luk
Mapa
Meč
Šíp
Štít
Aj tentoraz zostáva pôvodný zoznam nezmenený.
print(backpack)
Výstup:
['Meč', 'Jablko', 'Mapa', 'Štít', 'Elixír', 'Luk', 'Šíp']
Funkcia sorted() vracia nový usporiadaný zoznam, zatiaľ čo pôvodný zoznam ponechá bez zmien. Smer triedenia možno navyše jednoducho otočiť.
Zoradenie od konca abecedy: reverse=True
Ak chce Janko začať od konca abecedy, funkcii sorted() odovzdá parameter reverse=True.
for item in sorted(backpack, reverse=True):
print(item)
Výstup:
Štít
Šíp
Meč
Mapa
Luk
Jablko
Elixír
Krátke anonymné funkcie: lambda
Doteraz o spracovaní predmetov rozhodovali najmä vstavané funkcie a ich parametre. Pri filtrovaní alebo úprave výbavy však Janko potrebuje zadať vlastné pravidlo. Ak je takéto pravidlo krátke a použije sa iba raz, môže ho zapísať ako anonymnú funkciu, nazývanú aj lambda funkcia.
Predstavte si, že čarodejník potrebuje očarovať jeden meč. Nemá zmysel vytvárať pomenovanú funkciu, ak ju použije iba raz.
Bežná funkcia vyzerá takto:
def enchant(item):
return "Magický " + item
print(enchant("Meč"))
Výstup:
Magický Meč
To isté môžete zapísať kratšie pomocou lambda.
enchant = lambda item: "Magický " + item
print(enchant("Meč"))
Výstup:
Magický Meč
Lambda funkcia má jednoduchú syntax:
lambda parametre: výraz
Na rozdiel od bežnej funkcie:
- nemá meno (preto sa nazýva anonymná),
- neobsahuje príkaz
return– výsledok výrazu vráti automaticky, - môže obsahovať iba jeden výraz, nie viacero príkazov.
Napríklad funkcia, ktorá zdvojnásobí číslo:
double = lambda x: x * 2
print(double(5))
Výstup:
10
Lambda funkcie sa najčastejšie používajú spolu s funkciami map(), filter() alebo reduce(). Umožňujú im priamo odovzdať jednoduchú operáciu bez vytvárania samostatnej pomenovanej funkcie.
Napríklad pri odstraňovaní jabĺk zo zoznamu:
items = ["Jablko", "Meč", "Jablko", "Štít"]
valuable = list(filter(
lambda item: item != "Jablko",
items
))
print(valuable)
Tu výraz
lambda item: item != "Jablko"
znamená:
„Pre každý prvok s názvom
itemvráť hodnotuTrue, ak to nie je Jablko, inakFalse.“
Funkcia filter() potom ponechá iba tie prvky, pre ktoré lambda funkcia vrátila hodnotu True.
Poznámka: Lambda funkcie sú vhodné iba pre krátke a jednoduché operácie. Ak funkcia obsahuje viac krokov alebo zložitejšiu logiku, je prehľadnejšie použiť klasickú funkciu vytvorenú pomocou
def.
Výber prvkov pomocou filter()
Prvým praktickým využitím lambda funkcie bude výber užitočnej výbavy. Janko už nepotrebuje jablká, preto pomocou funkcie filter() vytvorí nový zoznam bez nich.
items = ["Jablko", "Meč", "Jablko", "Štít", "Elixír"]
valuable = list(filter(
lambda item: item != "Jablko",
items
))
print(valuable)
Výstup:
['Meč', 'Štít', 'Elixír']
Funkcia filter() teda rozhoduje, ktoré prvky zostanú. Ak chcete každý
prvok zmeniť, použite funkciu map().
Premena prvkov pomocou map()
Miestny čarodejník teraz očaruje všetky Jankove zbrane. Rovnakú úpravu má vykonať nad každým prvkom zoznamu, preto použije funkciu map().
items = ["Meč", "Štít", "Luk"]
magic_items = list(map(
lambda item: "Magický " + item,
items
))
print(magic_items)
Výstup:
['Magický Meč', 'Magický Štít', 'Magický Luk']
Výsledkom je opäť zoznam, tentoraz s premenenými prvkami. Niekedy však Janko nepotrebuje ďalší zoznam, ale jediný spoločný výsledok.
Výpočet jednej hodnoty pomocou reduce()
Predpokladajme, že každý nájdený poklad má určitú hodnotu a Janko chce poznať hodnotu celej zbierky.
values = [150, 40, 80, 120]
Na výpočet celkovej hodnoty použite funkciu reduce() z modulu functools.
from functools import reduce
total = reduce(
lambda a, b: a + b,
values
)
print(total)
Výstup:
390
Funkcia reduce() spája hodnoty po dvojiciach, až kým nezostane jediný výsledok. Výpočet preto prebieha postupne.
150 + 40 = 190
190 + 80 = 270
270 + 120 = 390
Tvorba zoznamov pomocou zoznamovej notácie
Funkcie filter() a map() dokážu zoznam vybrať alebo premeniť. Pri jednoduchých úlohách však Python ponúka aj stručnejší zápis. Najprv sa pozrime na vytvorenie zoznamu druhých mocnín klasickým cyklom.
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
squares.append(n * n)
print(squares)
Ten istý výsledok môžete zapísať kratšie a čitateľnejšie pomocou zoznamovej notácie (list comprehension).
numbers = [1, 2, 3, 4, 5]
squares = [n * n for n in numbers]
print(squares)
Výstup:
[1, 4, 9, 16, 25]
Zoznamová notácia môže obsahovať aj podmienku, a tak do nového zoznamu vybrať iba niektoré prvky.
numbers = [1, 2, 3, 4, 5, 6]
even = [n for n in numbers if n % 2 == 0]
print(even)
Výstup:
[2, 4, 6]
V jedinom zápise možno výber a premenu aj skombinovať.
numbers = [1, 2, 3, 4, 5, 6]
double_even = [n * 2 for n in numbers if n % 2 == 0]
print(double_even)
Výstup:
[4, 8, 12]
Zoznamová notácia tak dokáže v jednom čitateľnom výraze vytvoriť nový zoznam, vybrať potrebné prvky a zároveň ich premeniť.
N-tice (tuples)
Janko má niektoré údaje, ktoré sa po vytvorení nemajú meniť — napríklad súradnice pokladu alebo rozmery mapy. Na takéto údaje sa hodí n-tica (tuple). Je to usporiadaná kolekcia podobná zoznamu, ale jej prvky nie je možné meniť.
N-ticu zapíšte do okrúhlych zátvoriek a jednotlivé prvky oddeľte čiarkou:
coordinates = (48.7164, 21.2611)
treasure = ("Zlatý prsteň", 250, True)
print(coordinates)
print(treasure[0])
Výstup:
(48.7164, 21.2611)
Zlatý prsteň
Rovnako ako pri zozname môžete používať indexovanie, výrezy, funkciu len(),
operátor in a cyklus for:
items = ("Meč", "Štít", "Luk")
print(items[1]) # Štít
print(items[:2]) # ('Meč', 'Štít')
print(len(items)) # 3
print("Luk" in items) # True
for item in items:
print(item)
N-tica je nemenná (immutable). Po jej vytvorení nemôžeme prepísať, pridať ani odstrániť prvok:
items = ("Meč", "Štít")
items[0] = "Luk" # TypeError
Ak potrebujete údaje zmeniť, vytvorte novú n-ticu alebo použite zoznam:
items = ("Meč", "Štít")
items = items + ("Luk",)
print(items)
Čiarka za jediným prvkom je dôležitá. Bez nej by išlo iba o obyčajný výraz v zátvorkách:
one_item = ("Meč",) # n-tica s jedným prvkom
not_a_tuple = ("Meč") # reťazec
N-tice podporujú aj rozbaľovanie (unpacking). Hodnoty z n-tice priradíme viacerým premenným naraz:
student = ("Janko", 20, "Košice")
name, age, city = student
print(f"{name} má {age} rokov a býva v meste {city}.")
Ak funkcia vracia viac hodnôt, Python ich v skutočnosti môže vrátiť ako n-ticu:
def min_max(numbers):
return min(numbers), max(numbers)
smallest, largest = min_max([4, 8, 2, 9])
print(smallest, largest) # 2 9
Zoznam používame vtedy, keď sa kolekcia bude meniť. N-tica je vhodná pre údaje, ktoré majú zostať v rovnakom poradí a nemajú sa meniť. N-tica môže obsahovať aj zoznam, no samotná referencia na tento zoznam zostáva súčasťou n-tice:
data = ("Janko", ["Python"])
data[1].append("Databázy") # zoznam vo vnútri sa zmenil
print(data)
Cvičenia
- Vytvorte zoznam čísel a vypíšte iba čísla väčšie ako 10.
- Odstráňte zo zoznamu všetky výskyty zadanej hodnoty.
- Zo zoznamu mien vytvorte nový zoznam mien začínajúcich na písmeno
A. - Vytvorte slovník, v ktorom bude pri každom predmete uložená jeho cena, a pomocou zoznamovej notácie vypíšte iba predmety s cenou nad 50.
Zhrnutie
Janko a Marienka použili zoznam ako batoh, ktorého obsah môžu priebežne
meniť. Zoznam je usporiadaná a meniteľná kolekcia. Na bežnú prácu používajte
najmä append(), extend(), insert(), remove(), pop(), sorted() a
zoznamovú notáciu. Všímajte si, ktoré operácie menia pôvodný zoznam a ktoré
vytvárajú nový. N-tica je usporiadaná, ale nemenná kolekcia vhodná pre údaje,
ktoré sa po vytvorení nemajú meniť.
Ďalšie zdroje
Ak si chcete prácu so zoznamami precvičiť alebo preštudovať podrobnejšie, pokračujte týmito materiálmi:
- Dive Into Python 3 – Lists – praktický výklad vytvárania, rozširovania, prehľadávania a výrezov zoznamov;
- Oficiálny manuál Pythonu – Data Structures – presný prehľad metód zoznamov, zoznamovej notácie a ďalších dátových štruktúr;
- Oficiálny manuál Pythonu – Lambda expressions – syntax a význam anonymných funkcií;
- Functional Programming HOWTO – úvod do iterátorov, generátorov, zoznamovej notácie a funkcionálneho štýlu v Pythone;
functools– Higher-order functions and operations on callable objects.