Lazy iterators and itertools

itertools · Essentials · 8 min

Every itertools function builds on these two ideas; misunderstanding them causes empty-result bugs.

Every function in itertools takes iterables and returns an iterator: an object that produces values one at a time when you ask for them with next() or a for loop. Nothing is computed up front. That's laziness, and it's why itertools can handle files larger than memory or infinite sequences.

Two consequences:

  • Nothing happens until you consume. itertools.chain(a, b) returns immediately; the work happens as you iterate. To see the values, loop over them, or wrap with list() (only for finite, reasonably small results).
  • Iterators are single-use. After you've looped over an iterator once, it's exhausted. A second loop produces nothing, silently. Lists, tuples and ranges are iterables you can loop over again; the iterators made from them are not.

Pass the result of an itertools function directly into the next function, sum(), max(), "".join() or a for loop. If you need to iterate twice, build a list once, or call the itertools function again.

next(it, default) takes one item, returning default instead of raising StopIteration when the iterator is exhausted.

itertools is implemented in C, so these functions are fast; the pipeline overhead is usually lower than an equivalent hand-written generator.

Example

from itertools import chain

evens = chain([0, 2], [4, 6])
print(evens)                 # <itertools.chain object at 0x...>
print(next(evens))           # 0
print(list(evens))           # [2, 4, 6]
print(list(evens))           # []  -- exhausted

nums = [3, 1, 2]
it = iter(nums)
print(sum(it), sum(it))      # 6 0
print(sum(nums), sum(nums))  # 6 6  (a list is re-iterable)
print(next(iter([]), "none"))  # none

Remember

  • itertools functions return lazy, single-use iterators
  • Consume with a loop, sum/max/join, or list() for small finite results
  • Lists can be iterated repeatedly; iterators cannot

Watch out for

  • Iterating an itertools result twice and getting nothing the second time.

Check yourself

  1. it = map(str.upper, ['a', 'b']); x = list(it); y = list(it). What is y?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/itertools.html