Skip to content

Itertools

itertools is a standard-library module of fast, memory-efficient iterator building blocks. itertools.chain(*iterables) is the most commonly reached-for one: it lazily iterates over several iterables in sequence, as if they were one, without first concatenating them into a new list.

list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
combined = list1 + list2 + list3 # builds a whole new list in memory
for x in combined:
process(x)
from itertools import chain
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
for x in chain(list1, list2, list3): # no intermediate list built
process(x)
print(list(chain(list1, list2, list3))) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# chain.from_iterable β€” for when you already have an iterable of iterables
matrix = [[1, 2], [3, 4], [5, 6]]
flat = list(chain.from_iterable(matrix))
print(flat) # [1, 2, 3, 4, 5, 6]
  • Lazy, not eager: chain() yields items one at a time as you iterate β€” no new combined list is materialized in memory, which matters when the inputs are large or are themselves generators.
  • Works across mixed iterable types: chain a list, a range, a generator, and a set together without converting any of them first.
  • Composable: itertools functions are designed to feed into each other β€” chain, islice, groupby, zip_longest, product, combinations β€” building complex iteration pipelines without writing manual loops.
  • chain(a, b, c) takes the iterables as separate arguments; chain.from_iterable([a, b, c]) takes a single iterable of iterables β€” pick based on whether you already have a container of the inputs.
  • Because chain() returns an iterator, you can only consume it once β€” list(chained) twice gives [] the second time.
  • chain() doesn’t check for duplicates or sort anything β€” it’s purely sequential concatenation-by-iteration.
c = chain([1, 2], [3, 4])
print(list(c)) # [1, 2, 3, 4]
print(list(c)) # [] -- already exhausted
  1. What does itertools.chain([1,2], [3,4], [5]) produce when converted to a list?

    Answer`[1, 2, 3, 4, 5]` β€” it iterates each input in sequence as if they were one iterable.
  2. Why is chain() more memory-efficient than list1 + list2?

    Answer`chain()` yields items lazily as you iterate, without ever building a new combined list; `+` allocates a brand-new list holding every element up front.
  3. When would you use chain.from_iterable instead of chain?

    AnswerWhen you already have a single iterable *containing* the iterables to flatten (e.g. a list of lists), rather than the iterables as separate arguments.