Python-iterators: hoe ze werken en hoe je je eigen maakt
Python-iterators
Ontdek hoe Python-iterators functioneren, hoe je ze doorloopt met for-lussen of de next()-functie, en hoe je custom of oneindige iterators maakt met het iterator-protocol.
Video: Python-iterators
Iterators in Python
Iterators zijn alomtegenwoordig in Python. Ze liggen ten grondslag aan for-lussen, list comprehensions, generators en veel ingebouwde datastructuren, vaak werkend achter de schermen.
Een iterator is simpelweg een object dat data één element per keer oplevert. In Python wordt elk object dat de __iter__()- en __next__()-methoden implementeert een iterator genoemd.
Wanneer een object een iterator kan leveren, heet het iterable. Meeste ingebouwde containers – lijsten, tuples, strings, dictionaries – zijn iterable. De ingebouwde iter()-functie roept een object's __iter__()-methode aan om een iterator te verkrijgen.
Itereren door een iterator
Je kunt een iterator handmatig voortzetten met de next()-functie. Zodra de iterator uitgeput is, wordt StopIteration gegooid.
# definieer een lijst
my_list = [4, 7, 0, 3]
# krijg een iterator met iter()
my_iter = iter(my_list)
# itereren erdoor met next()
# Uitvoer: 4
print(next(my_iter))
# Uitvoer: 7
print(next(my_iter))
# next(obj) is hetzelfde als obj.__next__()
# Uitvoer: 0
print(my_iter.__next__())
# Uitvoer: 3
print(my_iter.__next__())
# Dit zal fout geven, geen items meer
next(my_iter)
Uitvoer
4 7 0 3 Traceback (most recent call last): File "<string>", line 24, in <module> next(my_iter) StopIteration
Een natuurlijkere manier om te itereren is met een for-lus, die de iterator-lifecycle automatisch afhandelt.
>>> for element in my_list:
... print(element)
...
4
7
0
3
Hoe de for-lus met iterators werkt
De for-lus vertaalt intern naar:
for element in iterable:
# doe iets met element
wat equivalent is aan de volgende logica:
# maak een iterator-object van dat iterable
iter_obj = iter(iterable)
# oneindige lus
while True:
try:
# haal het volgende item op
element = next(iter_obj)
# doe iets met element
except StopIteration:
# als StopIteration gegooid wordt, break uit lus
break
Dit toont dat de for-lus essentieel een oneindige lus is die vertrouwt op het iterator-protocol en de StopIteration-exception om te termineren.
Eigen iterators bouwen
Een iterator vanaf nul maken is eenvoudig: implementeer __iter__() om de iterator-instantie te retourneren (vaak self) en __next__() om de volgende waarde op te leveren of StopIteration te gooien wanneer klaar.
Hieronder een voorbeeld dat opeenvolgende machten van twee oplevert tot een door de gebruiker gespecificeerde maximum-exponent.
class MachtTwee:
"""Iterator die machten van twee oplevert."""
def __init__(self, max=0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration
# maak een object
getallen = MachtTwee(3)
# maak een iterable van het object
i = iter(getallen)
# next gebruiken om naar volgende iterator-element te gaan
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i))
Uitvoer
1 2 4 8 Traceback (most recent call last): File "/home/bsoyuj/Desktop/Untitled-1.py", line 32, in <module> print(next(i)) File "<string>", line 18, in __next__ raise StopIteration StopIteration
Een for-lus met de custom iterator is eveneens simpel:
>>> for i in MachtTwee(5):
... print(i)
...
1
2
4
8
16
32
Python Oneindige Iterators
Iterators kunnen onbegrensd zijn. De ingebouwde iter() accepteert een callable en een sentinel-waarde: het roept de functie herhaaldelijk aan tot de geretourneerde waarde gelijk is aan de sentinel. Bijvoorbeeld:
>>> int()
0
>>> oneindig = iter(int,1)
>>> next(oneindig)
0
>>> next(oneindig)
0
Omdat int() altijd 0 retourneert, ziet iter(int, 1) nooit de sentinel, wat een oneindige iterator produceert.
Je kunt ook je eigen oneindige iterator maken, zoals één die alle oneven getallen oplevert:
class OneindigIter:
"""Oneindige iterator die opeenvolgende oneven getallen retourneert."""
def __iter__(self):
self.num = 1
return self
def __next__(self):
num = self.num
self.num += 2
return num
Voorbeeldrun:
>>> a = iter(OneindigIter())
>>> next(a)
1
>>> next(a)
3
>>> next(a)
5
>>> next(a)
7
Bij werken met oneindige iterators, bied altijd een beëindigingsconditie om oneindige lussen te vermijden.
Iterators zijn geheugenefficiënt omdat ze items on-demand produceren. Je kunt theoretisch een onbegrensde sequentie genereren – zoals alle oneven getallen – zonder de hele set op te slaan.
Voor een simpelere manier om iterators te maken, verken Python generators using yield.
Python
- Python Datatypes onder de knie krijgen: Een praktische gids
- Python Operators onder de knie krijgen: Een uitgebreide gids
- Python Pass Statement onder de knie krijgen: Een praktische gids
- Python Functie-argumenten onder de knie krijgen: Positionele, Keyword en Default Parameters
- Python-dictionaries beheersen: aanmaken, manipuleren en geavanceerde technieken
- Python-closures uitgelegd: hoe geneste functies variabelen vangen
- Python datetime beheersen: praktische gids voor datums, tijden en tijdzones
- Python's time.sleep() onder de knie krijgen: best practices, multithreading en real-world voorbeelden
- Python Overzicht: Hoog-Niveau, Leesbaar, Interactief & Object-Georiënteerd
- Python Numbers: Immutable Numerieke Datatypes Begrijpen
- Python Strings Beheersen: Aanmaken, Benaderen en Manipuleren