Generátory

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().

Úroveň znalostí
Začátečník