Python course menu
Python Iterators: iter(), next() and the Iterator Protocol
Understand how Python loops really work: iterables vs iterators, iter() and next(), StopIteration, building your own iterator class with __iter__ and __next__, one-time iterators, and useful tools from itertools.
On this page
Every for loop in Python relies on two simple ideas: iterables and iterators. Understanding them explains why you can loop over lists, strings, files and dictionaries in exactly the same way, and lets you make your own objects loopable.
Iterable vs iterator#
iter() and next()#
iter() gets an iterator from an iterable; next() asks it for the next value:
nums = [10, 20, 30]
it = iter(nums)
print(next(it))
print(next(it))
print(next(it))
print(next(it))10
20
30
StopIterationWhen there are no values left, the iterator raises StopIteration.
What a for loop really does#
This loop:
for n in nums:
print(n)works roughly like this:
it = iter(nums)
while True:
try:
n = next(it)
except StopIteration:
break
print(n)for calls iter() once, calls next() repeatedly, and stops quietly at StopIteration. Any object that supports this iterator protocol works in a for loop, in list(), sum(), zip(), unpacking, and more.
Iterators are used up#
An iterator remembers its position and does not rewind:
it = iter([10, 20, 30])
for n in it:
print(n)
print(list(it))10
20
30
[]The second pass gets nothing. A list is different: each loop calls iter() again and gets a fresh iterator. The same applies to map, filter and zip objects, which are iterators:
r = range(3)
print(list(r), list(r)) # range is an iterable: reusable
m = map(str, [1, 2, 3])
print(list(m), list(m)) # map is an iterator: one pass only[0, 1, 2] [0, 1, 2]
['1', '2', '3'] []Building your own iterator#
A class becomes an iterator by defining two special methods:
__iter__returns the iterator itself.__next__returns the next value, or raisesStopIterationwhen finished.
class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
value = self.current
self.current -= 1
return value
for n in Countdown(3):
print(n)
print(list(Countdown(5)))3
2
1
[5, 4, 3, 2, 1]Like all iterators, one Countdown object can be consumed only once:
c = Countdown(2)
print(list(c), list(c))[2, 1] []Writing iterator classes by hand is a bit verbose; generators do the same job in a few lines, and are what most Python code uses.
Infinite and lazy iterators#
Iterators produce values on demand (lazily), so they can even be infinite. The itertools module has many ready-made ones:
import itertools
print(list(itertools.islice(itertools.count(10, 5), 4))) # 10, 15, 20, ... (first 4)
print(list(itertools.chain([1, 2], (3, 4)))) # join iterables
print(list(itertools.zip_longest("ab", "xyz", fillvalue="-"))) # zip without truncating
for combo in itertools.combinations(["A", "B", "C"], 2):
print(combo)[10, 15, 20, 25]
[1, 2, 3, 4]
[('a', 'x'), ('b', 'y'), ('-', 'z')]
('A', 'B')
('A', 'C')
('B', 'C')islice takes the first few values of an iterator, which is essential for infinite ones like count().
Common mistakes#
Practice#
Practice exercise
Write an iterator class EvenNumbers(limit) that produces 2, 4, 6, … up to and including limit. Print list(EvenNumbers(10)).
Show hint
Start a counter at 0 in __init__. In __next__, add 2 and raise StopIteration once it passes the limit.
Show solution
class EvenNumbers:
def __init__(self, limit):
self.limit = limit
self.n = 0
def __iter__(self):
return self
def __next__(self):
self.n += 2
if self.n > self.limit:
raise StopIteration
return self.n
print(list(EvenNumbers(10)))[2, 4, 6, 8, 10]Key takeaway#
Finished this lesson?