přejít na obsah přejít na navigaci

Linux E X P R E S, Python 3 (11): iterátory a generátory

Python 3 (11): iterátory a generátory

python.png

V dnešnej časti si ukážeme čo to sú a ako fungujú iterátory či už jednotlivých iterovateľných dátových typov alebo si ich vyrobíme z vlastnej triedy. Potom si povieme niečo o generátoroch, ktoré si porovnáme s iterátormi. Nakoniec si spomenieme aj „Generator Expressions“ a „List Comprehensions“.


Iterátory

Všetky iterovateľné objekty, čiže zoznamy, sety, slovníky, reťazce atp. – dátové typy, ktoré môžeme prechádzať element po elemente napríklad cyklom for…:

>>> l = [47, "Ahoj.", [], False]
>>> for element in l: print(element)
... 
47
Ahoj.
[]
False

… môžeme premeniť na ich iterátory, a to pomocou vstavanej funkcie iter(). Tá volá metódu __iter__(), ktorú nájdeme len pri iterovateľných objektoch, a tá vráti iterátor daného objektu:

>>> li = iter(l)
>>> li
<list_iterator object at 0x7f38eebc4400>

V tomto prípade sme dostali objekt triedy (typu) list_iterator. Pokiaľ by sme iterátor vytvárali z iného dátového typu, vytvorili by sme iterátor daného iterovateľného dátového typu:

iter("Ahoj.")		-> str_iterator
iter({2, 3})		-> set_iterator
iter(('a', 'b'))	-> tuple_iterator
iter({1:"Jedna"})	-> dict_keyiterator (iterátor je vytvorený z kľúčov slovníku)

Iterátory sa však pri každom z typov správajú rovnako. Základnou schopnosťou iterátoru je možnosť použitia funkcie next(), ktorá vracia vždy prvok iterátoru, ktorý je nasledujúci v poradí, čiže si pamätá svoju pozíciu.

>>> next(li)
47
>>> next(li)
'Ahoj.'
>>> next(li)
[]
>>> next(li)
False
>>> next(li)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Chyba StopIteration značí to, že sme vyčerpali všetky prvky iterátoru.

Iterátor, resp. objekt, ktorý budeme iterovať, si môžeme vytvoriť aj sami. Objekt samozrejme vytvárame z triedy, takže si napíšeme triedu, z ktorej vytvoríme objekt, ktorý nám bude postupne vracať čísla fibonacciho postupnosti. Donekonečna.

Trieda pre vytvorenie iterátora by mala obsahovať metódy __iter__() a __next__().

# iter_fib.py
class fib:
    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        a = self.a
        self.a, self.b = self.b, a + self.b
        return a

Z tejto triedy teraz vytvoríme objekt – iterátor.

>>> from iter_fib import fib
>>> it_fib = fib()
>>> next(it_fib)
0
>>> next(it_fib)
1
>>> next(it_fib)
1
>>> next(it_fib)
2
>>> next(it_fib)
3
>>> next(it_fib)
5
>>> next(it_fib)
8

Volať funkciu next() na objekt it_fib, čiže volať metódu __next__() objektu it_fib, by sme mohli donekonečna, a získať tak neobmedzené množstvo čísiel z fibonacciho postupnosti.

 


Generátory

Generátor je podobný iterátoru, no vytvárame ho pomocou funkcie, ktorá namiesto vrátenia hodnoty pomocou return používa príkaz yield. Takto vyzerá jednoduchá funkcia pre vytvorenie generátoru:

>>> def fce():
...  yield 1
...  yield 2
...  yield 3
... 
>>> fce # funkcia s yield sa tvári ako každá iná funkcia, ale funguje rozdielne
<function fce at 0x7f142e646f28>
>>> generator = fce()
>>> generator
<generator object fce at 0x7f142d3b1468>

Vidíme, že funkcia fce() pri zavolaní vrátila objekt generator object fce, aj keď navonok sa tvári ako klasická funkcia. Tento vytvorený objekt generátora je iterovateľný a môžeme ho teda prechádzať cyklom for, použiť naň funkciu next() alebo ho uložiť napríklad do listu (pokiaľ nieje nekonečný – to by nebol dobrý nápad).

>>> for x in generator:
...  print(x)
... 
1
2
3
>>> generator = fce()
>>> next(generator)
1
>>> next(generator)
2
>>> next(generator)
3
>>> next(generator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Podobne ako pri iterátoroch, pokiaľ sa snažíme z generátoru vytiahnuť ďalší už neexistujúci prvok, dostaneme chybu StopIteration.

Vytvorenie generátoru, ktorý bude postupne vracať čísla fibonacciho postupnosti je o niečo jednoduchšie ako v prípade iterátorov:

# yi_fib.py
def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

Z tohto nám vznikne generátor fibonacciho postupnosti až po nekonečno, my si však vypíšeme len čísla po 6:

>>> from yi_fib import fib
>>> fibonacci = fib()
>>> x = next(fibonacci)
>>> while x<6:
...  print(x)
...  x = next(fibonacci)
... 
0
1
1
2
3
5

Zapisujeme generátor ako výraz

Pre zapísanie generátoru môžeme použiť tzv. Generator Expressions. Tie zapisujeme do jedného riadku, a vyzerajú nejak takto:

>>> ge = (x*(1+x) for x in range(5))
>>> ge
<generator object <genexpr> at 0x7f5c7b4fe4c0>
>>> for x in ge:
...  print(x)
... 
0
2
6
12
20

Keď hore uvedený príklad zapíšeme pomocou funkcie s yield, bude vyzerať takto:

>>> def unforgettablename():
...  for x in range(5):
...   yield x*(1+x)
... 
>>> gen = unforgettablename()
>>> gen
<generator object unforgettablename at 0x7f5c7b4fe468>
>>> for x in gen:
...  print(x)
... 
0
2
6
12
20

„List Comprehensions“

Keď vytvoríme generátor, jeho prvky sú generované postupne a počas behu, čo nám šetrí veľké množstvo pamäti. Avšak kvôli tomu z generátoru nevieme jednoducho vybrať nejaký prvok, bez toho, aby sme ho najprv previedli napríklad na list. List môžeme ale vygenerovať podobne ako generátor z Generator Expression, len musíme vymeniť zátvorky. Dostaneme tak celý zoznam prvkov, ktorý je vygenerovaný práve v tom okamihu, a negeneruje sa postupne.

>>> ge = (x*(1+x) for x in range(5))
>>> lc = [x*(1+x) for x in range(5)]
>>> type(ge)
<class 'generator'>
>>> type(lc)
<class 'list'>
>>> ge[2]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'generator' object is not subscriptable
>>> lc[2]
6

A príklad toho, ako nám generátor ušetrí kopu pamäti:

>>> ge = (x**x for x in range(100))
>>> lc = [x**x for x in range(100)]
>>> from sys import getsizeof # sys.getsizeof() v dokumentácii
>>> getsizeof(ge)
88
>>> getsizeof(lc)
912
>>> lc
[1, 1, 4, 27, 256, 3125, 46656, 823543, 16777216, 387420489, …, …]

V ďalších častiach

V budúcej dvanástej časti sa pozrieme na zaujímavú vecičku, ktorou sú dekorátory.

Do ďalších častí následne už plánujem spracovať príklady na použitie niektorých zaujímavých modulov, či už na prístup k databázam alebo vytváranie jednoduchých webových aplikácií.

Nahoru

Příspěvky

Python 3 (11): iterátory a generátory
jnet 14. 12. 2015, 10:35:04
Odpovědět  Odkaz 
Toto byl opravdu povedený díl seriálu. Když jsem se iterátory v Pythonu zabýval, tak jsem si z mnoha internetových zdrojů musel nejdříve poskládat odpovědi na otázky proč se to vlastně zavedlo, jak se to používá a jak se ve vlastních programech využijí jejich výhody. Pokud někdo chce začít s Pythonem přes Diving in Python3, tak bych mu nejprve doporučil tento článek a pak na příkladech z této knihy už mu bude vše jasné.

Docela jsem zvědav na dekorátory, jsou už v C++ a i tam je taková "vyšší dívčí" tak jsem zvědav, jak to autor pojme ve stylu "Python pro dělníky a mistry", přeji mu v tom mnoho úspěchů.
Python 3 (11): iterátory a generátory
Wrunx 15. 12. 2015, 19:07:12
Odpovědět  Odkaz 
Při iteraci s "next" nemusíme po vyčerpání konečného iterátoru upadat nezbytně do chybového hlášení. Nekomu může třeba víc vyhovovat:

>>> a=iter("jo")
>>> next(a,"hotovo")
'j'
>>> next(a,"hotovo")
'o'
>>> next(a,"hotovo")
'hotovo'

Samosebou místo "hotovo" tam může být třeba None nebo tak...

Přidat názor

Nejsou podporovány žádné značky, komentáře jsou jen čistě textové. Více o diskuzích a pravidlech najdete v nápovědě.
Diskuzi můžete sledovat pomocí RSS kanálu rss



 
 

Top články z OpenOffice.cz

Štefan Uram




Public Relations

Soulad s normou nemusí znamenat bezpečnost

ALEFNemusíme se vracet daleko do minulosti, aby bylo možné pozorovat IT oddělní velkých společností, jak se soustředí téměř výhradně na efektivitu poskytovaných služeb pro primární účely organizace. Proč také ne? Informační bezpečnost byla výsadou několika málo oborů a většina organizací si vystačila s firewallem a antivirovým programem.

Pokračování ...


IBM POWER

Redakční blog

Pavel Fric

Pavel Fric, 21. August

Sayonara Player 1.5.1

Přehrávač, jak má být. Poslední dobou vývoj šlape


Pavel Fric

Pavel Fric, 26. January

MuseScore 3

První aktualizace třetí řady notačního editoru MuseScore


Redakce

Redakce, 21. December

Pište pro LinuxEXPRES

Baví vás Linux? Pište o něm, není to nic těžkého. LinuxEXPRES hledá nové autory.


Všechny blogy »