A generator olyan függvény vagy kifejezés, amely egy különleges típusú iterátort ad vissza, amelyet generátor-iterátornak nevezünk.
A Generator-iterators lusta: nem tárolja a values értékeit a memóriában, hanem generálja őket, amikor szükség van rájuk.
A generátorfüggvény úgy néz ki, mint bármely más függvény, de egy vagy több yield-kifejezést tartalmaz.
Minden yield felfüggeszti a kód végrehajtását, elmentve az aktuális végrehajtási állapotot (beleértve az összes lokális változót és a try-utasításokat).
Amikor a generátor folytatja a működést, onnan veszi fel az állapotot, ahol felfüggesztették, ellentétben a hagyományos függvényekkel, amelyek minden híváskor visszaállnak.
A generátorokat nagyon hasonlóan hozzuk létre, mint más ciklikus vagy rekurzív függvényeket, de szükség van bennük egy yield kifejezésre, amelyet kicsit később részletesen is megvizsgálunk.
Példa erre egy olyan függvény, amely egy adott számlistából visszaadja a négyzeteket.
A jelenlegi formájában az összes bemenetet fel kell dolgozni, mielőtt bármilyen értéket vissza lehet adni:
>>> def squares(list_of_numbers):
... squares = []
... for number in list_of_numbers:
... squares.append(number ** 2)
... return squares
Ezt a függvényt így alakíthatod generátorrá:
>>> def squares_generator(list_of_numbers):
... for number in list_of_numbers:
... yield number ** 2
Ennek az az oka, hogy akkor érdemes generátort használni, amikor nincs szükséged az összes értékre egyszerre.
Ez memóriát és feldolgozási teljesítményt takarít meg, mivel csak az az érték kerül kiszámításra, amelyen éppen dolgozol.
A generátorok (technikailag generator-iteratorok, lásd a lenti megjegyzést.) az iterator egyik fajtája, és a Pythonban bárhol használhatók, ahol iterator-t vagy iterable-t várunk.
Ide tartoznak azok a függvények vagy objektumok is, amelyek iterable-t/iterator-t várnak argumentumként.
Ha mélyebben bele szeretnél ásni, nézd meg a How to Make an Iterator in Python oldalt.
A generátor-iterátorok az iterátorok egy különleges részhalmazát alkotják.
Az Iterators az a mechanizmus, illetve protokoll, amely lehetővé teszi, hogy végigiteráljunk az iterable objektumokon.
A generátor-iterátorok és a gyakori Python iterables által visszaadott iterátorok nagyon hasonlóan viselkednek, de néhány fontos különbségre érdemes figyelni:
indexes-ük.
Nem hivatkozhatsz egy korábbi vagy későbbi értékre összeadással vagy kivonással, és nem használhatod a szögletes zárójeles ([]) jelölést vagy a szeletelést.len() függvénnyel, mert nincs hosszuk.generator-iterator összes értékét összegyűjtöd!A squares_generator() generátor használatához:
>>> squared_numbers = squares_generator([1, 2, 3, 4])
>>> for square in squared_numbers:
... print(square)
...
1
4
9
16
A generátoron belüli értékeket a next() függvénnyel is előállíthatod, illetve elérheted.
A next() meghívja egy generátorobjektum __next__() metódusát, „előrehaladva” vagy kiértékelve a generátor kódját egészen a yield kifejezéséig, amely aztán „kiadja”, vagyis visszaadja az értéket.
>>> squared_numbers = squares_generator([1, 2])
>>> next(squared_numbers)
1
>>> next(squared_numbers)
4
Amikor egy generator teljesen elfogyott, és nincs több visszaadható értéke, StopIteration hibát dob.
>>> next(squared_numbers)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
A generátorok az iterátorok egy különleges részhalmazát alkotják.
Az Iterators az a mechanizmus, illetve protokoll, amely lehetővé teszi, hogy végigiteráljunk az iterable objektumokon.
A generátorok és a gyakori Python iterables által visszaadott iterátorok nagyon hasonlóan viselkednek, de néhány fontos különbségre érdemes figyelni:
A generátorok egyirányúak; nincs „visszalépés” egy korábbi értékre.
A generátorokon való végigiterálás felemészti a visszaadott értékeket; nincs visszaállítás.
A generátorok (mivel lustán értékelődnek ki) nem rendezhetők és nem fordíthatók meg.
A generátoroknak nincs indexes-ük, ezért nem hivatkozhatsz egy korábbi vagy későbbi értékre összeadással vagy kivonással.
A generátorok nem használhatók a len() függvénnyel.
A generátorok lehetnek végesek vagy végtelenek, vigyázz, amikor egy végtelen generátor összes értékét összegyűjtöd.
A yield-kifejezés nagyon hasonlít a return kifejezéshez.
A return kifejezéssel ellentétben a yield egy adott ponton adja át az értékeket a hívónak, és felfüggeszti további értékek kiértékelését, illetve visszaadását mindaddig, amíg azokat nem kérik.
Amikor a yield kiértékelődik, szünetelteti a befoglaló függvény végrehajtását, és visszaadja a függvény értékeit abban az adott pillanatban.
A függvény ezután scope-ban marad, és amikor a __next__() meghívásra kerül, a végrehajtás addig folytatódik, amíg újra nem találkozik egy yield kifejezéssel.
A yield kifejezések használata függvényeken kívül tilos.
>>> def infinite_sequence():
... current_number = 0
... while True:
... yield current_number
... current_number += 1
>>> lets_try = infinite_sequence()
>>> lets_try.__next__()
0
>>> lets_try.__next__()
1
A generátorok sok helyen hasznosak.
Amikor egy nagy gyűjteménnyel dolgozol, lehet, hogy nem akarod az összes értékét a memory-be tenni.
Egy generátorral darabonként dolgozhatsz fel nagyobb adathalmazokat, ami memóriát takarít meg és javítja a teljesítményt.
A generátorok akkor is nagyon hasznosak, amikor egy folyamat vagy számítás összetett, költséges vagy végtelen:
>>> def infinite_sequence():
... current_number = 0
... while True:
... yield current_number
... current_number += 1
Mostantól, valahányszor meghívod a __next__() metódust az infinite_sequence objektumon, az a korábbi szám + 1 értékkel tér vissza.