4. diel - Úvod do kolekcií v Pythone
V tutoriále Python Kolekcie sa zameriame na úvodnú teóriu ku kolekciám. Po absolvovaní kurzu budeme rozumieť pokročilým zoznamom, budeme vedieť, čo je to tuple a čo sú slovníky. Tieto kolekcie sú veľmi užitočné na ukladanie a prácu s dátami v programoch. Naučíme sa ich vytvoriť a inicializovať, ako s nimi pracovať (napr. pridávať, odoberať prvky), ako sa na ne odkazovať a ako ich prechádzať. Vysvetlíme si tiež príklady použitia kolekcií v rôznych situáciách, aby sme si ukázali ich praktickú hodnotu. Pochopíme aj rozdiel medzi generickými a všeobecnými kolekciami.
Minimálne znalosti / Minimálne požiadavky
Pre tento kurz je nutné ovládať Základná konštrukcia jazyka a Objektovo orientované programovanie v Pythone.
Kolekcia v Pythone
Pojem kolekcia označuje v Pythone dátové typy, ktoré umožňujú ukladať viac hodnôt v jednom objekte. Medzi najbežnejšie kolekcie v Pythone patria zoznamy, tuple, slovníky a množiny. Kolekcií však existuje viac a hoci sa zvonku často tvária podobne, vo vnútri fungujú veľmi odlišne. Vyberáme si ich teda podľa konkrétneho účelu.
Hash table a kolekcia
Python má niekoľko rôznych typov kolekcií, ako sú zoznamy, sady a slovníky. Tieto kolekcie sú založené na princípe hash table, čo je dátová štruktúra, ktorá umožňuje rýchle vyhľadávanie, pridávanie a odoberanie prvkov.
Hash table funguje tak, že pre každý prvok kolekcie sa vytvorí hash (číslo) na základe jeho hodnoty. Táto hodnota sa potom použije ako index, ktorý označuje, kde sa prvok uloží. Keď sa v budúcnosti potrebujeme k prvku vrátiť, vytvoríme znova hash a použijeme ho ako index pre jeho vyhľadanie. Tento proces je veľmi rýchly.
Genericita v Pythone
V Pythone sú generické typy implementované pomocou modulu
typing
, ktorý bol predstavený v Pythone 3.5. Modul
typing
nám umožňuje špecifikovať typy prvkov v kolekcii.
Generické kolekcie v Pythone sú špecifické pre daný typ,
čo znamená, že obsahujú iba prvky jedného typu. Cieľom generík v Pythone,
rovnako ako v iných programovacích jazykoch, je zlepšiť reusability
(opakovateľnosť) kódu a znížiť počet duplicitných častí. Generické
triedy a metódy umožňujú kódu pracovať s rôznymi typmi dát bez nutnosti
explicitného určenia typu. Jedna trieda alebo metóda teda môže byť
použitá pre rôzne typy dát bez nutnosti vytvorenia novej verzie pre každý
konkrétny typ. To umožňuje kódu byť flexibilnejší.
Treba však mať na pamäti, že keď definujeme premennú alebo atribút
triedy ako generický typ (napr. List[int]
), Python neskontroluje v
runtime, či premenná alebo atribút skutočne obsahujú prvky tohto typu.
Môžeme teda priradiť ľubovoľný typ hodnoty k premennej deklarovanej ako
generický typ a Python nevyvolá chybu. To ale môže viesť k chybám, pokiaľ
omylom premennej priradíme hodnotu zlého typu. Python sa tým líši od
staticky typovaných jazykov ako sú Java alebo C#, kde kompilátor skontroluje
v runtime, že premenná alebo atribút prvky daného typu skutočne obsahuje.
Pri omylom priradenej hodnote zlého typu Java alebo C# na rozdiel od Pythona
vyvolajú v runtime chybu.
V praxi teda môžeme povedať, že staticky typované jazyky majú väčšiu ochranu proti typovým chybám. Zároveň sú však viac obmedzujúce pri definovaní premenných a atribútov triedy, pretože je nutné špecifikovať ich typy explicitne. V Pythone máme teda väčšiu flexibilitu, ale súčasne to znamená, že musíme byť opatrnejší.
Type hints
Povedzme, že chceme vytvoriť kolekciu inštancií nejakej triedy. V Pythone (3.9) je to veľmi jednoduché. S použitím type hints to zvládneme veľmi rýchlo:
from dataclasses import dataclass @dataclass class Postava: jmeno: str level: int = 1 hrac1 = Postava("Sven Kladivo") hrac2 = Postava("Rudá Sonja", 2) host = "Není postava" postavy: list[Postava] = [hrac1, hrac2] print(postavy) postavy.append(host) print(postavy)
V konzole uvidíme výstup:
Konzolová aplikácia
[Postava(jmeno='Sven Kladivo', level=1), Postava(jmeno='Rudá Sonja', level=2)]
[Postava(jmeno='Sven Kladivo', level=1), Postava(jmeno='Rudá Sonja', level=2), 'Není postava']
Týmto spôsobom dokážeme vytvoriť kolekciu ľubovoľných objektov.
Užívateľom definované generické typy
V nasledujúcom príklade si vytvoríme triedu Rodina
. Typ
obsahu triedy je všeobecný a uvedieme ho vo chvíli, keď vytvárame
inštanciu tejto triedy. Po vytvorení inštancie bude táto inštancia na
rozdiel od predchádzajúceho príkladu prijímať iba argumenty tohto typu:
from typing import Dict, Generic, TypeVar T = TypeVar("T") class Rodina(Generic[T]): def __init__(self) -> None: self._uloz: Dict[str, T] = {} def nastav_polozku(self, k: str, v: T) -> None: self._uloz[k] = v def nacti_polozku(self, k: str) -> T: return self._uloz[k] if __name__ == "__main__": jmeno_pribuzneho = Rodina[str]() vek_pribuzneho = Rodina[int]() jmeno_pribuzneho.nastav_polozku("manželka", "Dana") jmeno_pribuzneho.nastav_polozku("dědeček", "Tomáš") vek_pribuzneho.nastav_polozku("Tomáš", 70)
Genericita nám teda umožnila vytvoriť triedu, ktorú je možné použiť s
viacerými typmi. Súčasne vďaka nástrojom ako je mypy
nedovolí, aby boli do metód inštancie odosielané iné argumenty ako tie
zadaného typu. Preto ak sa pokúsime o vloženie reťazca namiesto čísla do
veku:
vek_pribuzneho.nastav_polozku("Tomáš", "citron")
mypy
nám vynadá:
Konzolová aplikácia
Argument 2 to "nastav_polozku" of "Rodina" has incompatible type "str"; expected "int"
Využitie kolekcií v Pythone
Využitie je kolekciou je veľmi široké. Môžu byť použité pre rôzne úlohy a aplikácie, ako napríklad:
- práca s databázou: Slovníky a zoznamy sú často používané na ukladanie a prácu s dátami z databázy,
- textové procesy: Zoznamy a tuple sú často používané pre prácu s textom, napríklad pre rozdelenie textu na slová alebo prechádzanie textu po riadkoch,
- matematické operácie: Množiny sú často používané pre matematické operácie, ako je napríklad práca s množinou unikátnych hodnôt alebo zjednotenie alebo rozdiel množín,
- webové aplikácie: Slovníky a zoznamy sú často používané pre prácu s dátami z webových aplikácií, ako sú napríklad JSON alebo XML súbory,
- algoritmy: Zoznamy a tuple sú často používané na implementáciu rôznych algoritmov, ako sú napríklad prehľadávanie alebo radenie.
V budúcej lekcii, ChainMap, NamedTuple a DeQue v Pythone , si vysvetlíme, na čo a ako sa v Pythone používajú kolekcie ChainMap, NamedTuple a DeQue.