Generátory v Pythonu umožňují efektivně procházet, respektive generovat data. Abychom měli potřebný teoretický základ pro popis generátorů, začneme od iteratorů.
Některé python objekty, jako jsou například seznamy, slovníky nebo n-tice, dokážeme procházet a vyčítat si z nich uložené hodnoty. Tyto objekty implementují funkci __iter__
, která vrací iterator. Ten implementuje funkci __next__
, která při každém dalším zavolání vrací nějakou položku z iterovatelného objektu. Můžeme říct, že základní vlastnost iteratoru je "zapamatovat" si, jakou položku vrátil naposled a jakou má vrátit teď.
class OddNumbers:
def __init__(self, upto):
self.upto = upto
self.current_number = 1
def __iter__(self):
return self
def __next__(self):
current_number = self.current_number
self.current_number += 2
if current_number > self.upto:
raise StopIteration
return current_number
for i in OddNumbers(20):
print(i)
Objekt OddNumbers(20)
je zároveň iterovatelný objekt a iterator. Ve funkci __iter__
vrací instanci sebe sama a při každém průchodu cyklu se zavolá funkce __next__
. V objektu jako takovém si udržujeme aktuální stav pomocí atributu self.current_number
. For cyklus výše bychom mohli nahradit tímto kódem, který explicitně volá funkce iter()
a next()
:
odd_numbers_iter = iter(OddNumbers(20))
while True:
try:
current_item = next(odd_numbers_iter)
print(current_item)
except StopIteration:
break
Generátory
Generátory nám zjednodušují tvorbu iterátorů. Nemusíme implementovat třídu s příslušným rozhraním, ale vystačíme si s funkcí a klíčovým slovem yield
.
Pokud chceme z funkce vrátit nějakou hodnotu, běžně používáme klíčové slovo return
, po jehož zavolání se zároveň ukončí provádění funkce. Oproti tomu yield
hodnotu vrátí a zároveň na tomto místě dojde k zapauzování funkce a zapamatování vnitřního stavu. V podstatě to znamená, že při další iteraci se funkce bude vykonávat od tohoto řádků dál. Iterátor výše se dá pomocí generátoru takto zjednodušit:
def generate_odd_numbers(upto):
current_number = 1
while current_number < upto:
yield current_number
current_number += 1
for i in generate_odd_numbers(20):
print(i)
Stejně tak bude volat explicitní volání pomocí next()
a iter()
.