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

Altair

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“.


reklama

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

(Jako ve škole)
Průměr: 4,08 | Hodnotilo: 13
 

Top články z OpenOffice.cz

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



 
 

Štefan Uram

Som 19-ročný študent, ktorý študuje odbor informačné a sieťové technológie. O Linux sa zaujímam len niečo cez tri roky. Okrem Linuxu medzi moje záujmy patrí aj programovanie, Android a nekolektívne športy biliard a šípky.


  • Distribuce: Antergos
  • Grafické prostředí: Cinnamon & i3
  • Hodnocení autora: *

| blog



Public Relations

SUSECON 2017, světová konference o open source, poprvé v Praze!

Přihlaste se na globální akci zaměřenou na Linux, OpenStack, CEPH, Cloud Foundry, kontejnerová řešení Docker, NFV a další open source technologie.

Pokračování ...



Public Relations

Využijte letní akce na předplatné časopisu IT Systems!

Nenechte si ujít příležitost dostávat pravidelně informace ze světa podnikové informatiky a předplaťte si časopis IT Systems. Udržujte si přehled v oboru a získejte inspiraci, jak využít informační technologie pro vaši firmu nebo organizaci. K pořízení nebo obnovení předplatného IT Systems je nyní ideální příležitost. Právě totiž probíhá letní akce, v rámci níž je možno získat roční předplatné jen za 599 Kč.

Pokračování ...


Redakční blog

Pavel Fric

Pavel Fric, 28. únor

Lollypop


Pavel Fric

Pavel Fric, 29. listopad

Palapeli


Pavel Fric

Pavel Fric, 19. listopad

Amarok


Všechny blogy »