Iterators
Every for loop in Python relies on two ideas: iterables and iterators.
- An iterable is anything you can loop over: lists, strings, dictionaries, files, ranges.
- An iterator is the object that hands out the items one at a time and remembers where it is.
iter() and next()
Python
colours = ["red", "green", "blue"]
it = iter(colours) # get an iterator from the iterable
print(next(it))
print(next(it))
print(next(it))
print(next(it)) # nothing leftWhen an iterator runs out, next() raises StopIteration.
What a for loop does
This loop:
Python
for c in ["red", "green", "blue"]:
print(c)is, behind the scenes, roughly:
Python
it = iter(["red", "green", "blue"])
while True:
try:
c = next(it)
except StopIteration:
break
print(c)Iterators are used up
An iterator can only be gone through once:
Python
squares = map(lambda n: n * n, [1, 2, 3]) # map returns an iterator
print(list(squares))
print(list(squares)) # empty: it's already exhaustedLists can be looped over again and again, because each for loop asks for a new iterator.
Making a class iterable
Define __iter__ (returning an iterator) and __next__ (returning the next item or raising StopIteration):
Python
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(5):
print(n, end=" ")
print("Liftoff!")That works, but it's a lot of code for a simple idea. The next lesson shows generators, which do the same in a few lines.
Lazy iterables are memory-friendly
range, map, filter, zip, enumerate and file objects don't build their whole result in memory; they produce items on demand:
Python
big = range(10**12) # instant: nothing is stored
print(big[5], len(big))
evens = (n for n in big if n % 2 == 0)
print(next(evens), next(evens), next(evens))Finished this lesson?Mark it complete to earn +20 XP and move your progress on.