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() a reduce();
  • 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 item vráť hodnotu True, ak to nie je Jablko, inak False.“

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

  1. Vytvorte zoznam čísel a vypíšte iba čísla väčšie ako 10.
  2. Odstráňte zo zoznamu všetky výskyty zadanej hodnoty.
  3. Zo zoznamu mien vytvorte nový zoznam mien začínajúcich na písmeno A.
  4. 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:

Previous Post Next Post

Zoznamy: Jankov a Marienkin čarovný batoh