IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

4. diel - Iterátory v Pythone

V predchádzajúcom cvičení, Riešené úlohy k 2.-3. lekciu kolekcií v Pythone, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V tomto tutoriále kolekcií v Pythone sa zameriame na iterovateľné objekty a ich dôležitú podmnožinu- iterátory.

Iterovateľný objekt

Pomocou iterácie môžeme postupne získať prvky uložené v nejakej kolekcii (napríklad aplikácií for cyklu na zoznam). Iterovateľný objekt je potom taký objekt, na ktorom je možné vykonať iteráciu. Prvky získame buď v pevne stanovenom poradí (zoznam) alebo v poradí náhodnom (množina).

Iterátory v Pythone nám poskytujú výhodnú možnosť postupne spracovávať veľké množstvo dát, čo je obzvlášť užitočné pri práci s veľkými súbormi alebo dátovými prúdmi. Tieto nástroje nám umožňujú efektívnejšie organizovať naše kódy tým, že umožňujú použitie slučky for s rôznymi iterovateľnými objektmi, ako sú zoznamy, množiny, slovníky a súbory, pričom je dôležité si uvedomiť, že pri ich použití v slučke for Python automaticky vytvára iterátory z týchto iterovateľných štruktúr. Ďalej iterátory podporujú koncept lenivého vyhodnocovania, čo znamená, že prvky sú generované a spracovávané iba v okamihu, keď sú skutočne potrebné, čo v niektorých prípadoch výrazne zlepšuje výkon. V tejto lekcii sa podrobnejšie zoznámime s iterátormi v Pythone, naučíme sa, ako ich správne vytvárať a používať na efektívne spracovanie dát a preskúmame rôzne scenáre ich praktického využitia.

Iterátor

Iterátor je zodpovedný za iteráciu na iterovateľnom objekte. Pamätá si, ktoré prvky už poskytol (neposkytne jeden prvok dvakrát). Vo chvíli, keď už nie je ďalší prvok k dispozícii, nám to oznámi.

V tomto okamihu jeho úloha v programe končí, iterátor je tzv. vyčerpaný (exhausted). Ak chceme znova iterovať, musíme vytvoriť nový iterátor.

Trieda iterovateľného objektu musí implementovať špeciálnu metódu __iter__(), ktorá po zavolaní vytvorí a vráti novú inštanciu triedy Iterator (vytvorí nový „nevyčerpaný“ iterátor). Metódu môžeme volať cez vstavanú funkciu iter().

Trieda iterátora potom musí implementovať špeciálnu metódu __iter__(), ktorá ale vracia odkaz na svoju inštanciu – self (nevytvára novú inštanciu). Zároveň musí mať implementovanú metódu __next__(), ktorá po zavolaní vráti ďalší prvok z kolekcie. Pokiaľ už nie je ďalší prvok k dispozícii, vyvolá StopIteration výnimku. Metódu je možné volať vstavanou funkciou next().

Je teda veľký rozdiel, či iterujeme na iterovateľnom objekte (napr. zozname), ktorý zavolaním funkcie iter() vracia zakaždým nový iterátor, alebo priamo na iterátore, ktorý vracia iba sám seba.

Technicky je síce iterátor zároveň iterovateľný objekt (obaja implementujú metódu __iter__()). My ich ale budeme v tejto lekcii rozlišovať a pokiaľ budeme hovoriť o iterovateľnom objekte, budeme tým myslieť iterovateľný objekt, ktorý nie je zároveň iterátorom.

Cyklus for pod pokrievkou

Než sa vrhneme na praktické príklady, pozrime sa ešte, ako funguje cyklus for, ktorý je z hľadiska iterácie kľúčový. Python totiž v skutočnosti aplikuje cyklus while pomocou nasledujúceho mechanizmu:

seznam = [1, 2, 3, 4, 5]

iterator = iter(seznam)

try:
    while True:
        prvek = next(iterator)
except StopIteration:
    pass

Na začiatku zavolá funkciu iter() a dostane iterátor. Potom na ňom opakovane volá funkciu next() a získava jednotlivé prvky pokiaľ nenarazí na StopIteration výnimku. To sa dá aj jednoducho overiť. Vytvoríme si vlastnú triedu Seznam z triedy list a iba jej ľahko upravíme metódu __iter__(), aby sme vedeli, kedy bola volaná:

class Seznam(list):
    def __iter__(self):
        print("Volána metoda __iter__()")
        return super().__iter__()

Teraz vytvoríme jej inštanciu a použijeme for cyklus:

s = Seznam([1, 2, 3])

for prvek in s:
    print(prvek)

Vo výstupe vidíme, že pred vypisovaním jednotlivých prvkov je zavolaná metóda __iter__():

Konzolová aplikácia
Volána metoda __iter__()
1
2
3

Vstavané iterovateľné objekty a iterátory

Doteraz prebranú látku si najskôr vyskúšame na nami dobre známom zozname (objekte typu list). Vytvoríme jeho inštanciu a dvakrát po sebe necháme vypísať všetky jeho prvky:

horory = ["Vetřelec", "Frankenstein", "Věc"]

for horor in horory:
    print(horor)

for horor in horory:
    print(horor)

Všetko prebehlo v poriadku, pretože zoznam je iterovateľný objekt:

Konzolová aplikácia
Vetřelec
Frankenstein
Věc
Vetřelec
Frankenstein
Věc

Túto skutočnosť si zároveň môžeme overiť napríklad použitím vstavanej funkcie dir(), ktorá vracia zoznam atribútov príslušného objektu:

print("__iter__" in dir(horory), "__next__" in dir(horory))

Vo výstupe vidíme:

Konzolová aplikácia
(True, False)

Je teda zrejmé, že trieda list má definovanú metódu __iter__() ale nie metódu __next__(). Zavolaním funkcie iter() na náš zoznam získame jeho iterátor. Keďže zoznam je iterovateľný objekt, mal by vrátiť novú inštanciu iterátora, teda nie len odkaz na seba:

print(iter(horory) is horory)

Vo výstupe vidíme:

Konzolová aplikácia
False

Všetko teda prebieha podľa očakávania. Teraz si znova vytvoríme iterátor a uložíme si odkaz naň do premennej. Môžeme volať funkciu next(), poprípade metódu __next__() a získať postupne jeho prvky:

iterator_hororu = iter(horory)
print(next(iterator_hororu))
print(iterator_hororu.__next__())

Vo výstupe vidíme:

Konzolová aplikácia
Vetřelec
Frankenstein

V tom istom kóde môžeme ďalej použiť aj cyklus for. Len musíme pamätať na to, že iterátor sa postupne vyčerpáva:

for horor in iterator_hororu:
    print(horor)

Výstup:

Konzolová aplikácia
Věc

Keďže sme prvé dva prvky získali funkciou next(), for cyklus nám vrátil iba posledný prvok. Teraz je iterátor vyčerpaný a ak by sme chceli znova iterovať, museli by sme buď iterovať na pôvodnom zozname (ktorý si príslušný iterátor vytvorí sám automaticky) alebo si iterátor znova explicitne vytvoriť sami zavolaním funkcie iter().

Python ponúka niekoľko užitočných funkcií, ktoré vracajú iterovateľné objekty alebo iterátory. Pozrime sa na ne.

range()

Funkcia range() vracia objekt range, čo je iterovateľný objekt:

r = range(5)

print("__iter__" in dir(r), "__next__" in dir(r))

print(iter(r) is r)

Výstup:

Konzolová aplikácia
True False
False

Z výpisu je zrejmé, že objekt range má implementovanú iba metódu __iter__(), ktorá pred každou iteráciou vytvorí novú inštanciu iterátora. Iterovať na objekte range môžeme tým pádom bez obmedzenia:

print("První iterace:", end=" ")

for cislo in r:
    print(cislo, end=", ")

print("\n\nDalší iterace:", end=" ")

for cislo in r:
    print(cislo, end=", ")

Vo výstupe vidíme:

Konzolová aplikácia
První iterace: 0, 1, 2, 3, 4,

Další iterace: 0, 1, 2, 3, 4,

enumerate()

Oproti tomu funkcia enumerate() vracia objekt enumerate, čo je iterátor. Tento objekt má implementovanú ako metódu __iter__(), tak metódu __next__(). Funkcia iter() vracia ten istý objekt:

e = enumerate(["Homer", "Vočko", "Lenny", "Carl"])

print("__iter__" in dir(e), "__next__" in dir(e))

print(iter(e) is e)

Výstup:

Konzolová aplikácia
True True
True

Iterovať na objekte enumerate môžeme maximálne raz. Funkcia enumerate() vytvorí dvojice, kde prvou položkou je index a druhou položkou je príslušný prvok zadaného iterovateľného objektu:

e = enumerate(["Homer", "Vočko", "Lenny", "Carl"])
print("První iterace:", end=" ")

for postava in e:
    print(postava, end=", ")

print("\n\nDalší iterace:", end=" ")

for postava in e:
    print(postava, end=", ")

Vo výstupe vidíme:

Konzolová aplikácia
První iterace: (0, 'Homer'), (1, 'Vočko'), (2, 'Lenny'), (3, 'Carl'),

Další iterace:

Na záver si ukážme tabuľku najčastejšie používaných funkcií, ktoré vracajú iterovateľné objekty alebo iterátory:

Funkcia vracajúci iterovateľný objekt Funkcia vracajúci iterátor
list() enumerate()
tuple() zips()
set() máp()
dict() filter()
dict.keys() open()
dict.values()
dict.items()
range()
To by k tejto lekcii bolo všetko.

V ďalšej lekcii, Iterátory druhýkrát: Generátory v Pythone , si vytvoríme vlastný iterátor, zoznámime sa s generátormi a preskúmame ich výhody.


 

Predchádzajúci článok
Riešené úlohy k 2.-3. lekciu kolekcií v Pythone
Všetky články v sekcii
Kolekcia v Pythone
Preskočiť článok
(neodporúčame)
Iterátory druhýkrát: Generátory v Pythone
Článok pre vás napísal gcx11
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
(^_^)
Aktivity