Home/Python/Iterators and generators

Iterators and generators

Produce values lazily instead of storing every result

Iteration

An iterable supplies an iterator through iter(value). next(iterator) returns one item or raises StopIteration on exhaustion. A list can create fresh iterators; an iterator generally advances once and does not rewind.

Generators

A function containing yield creates a generator iterator when called. Its body starts on consumption; each yield suspends it with local state retained. The next request resumes after that yield. return ends iteration.

Python
def squares_below(limit):
    value = 0
    while value < limit:
        yield value * value
        value += 1

numbers = squares_below(3)
print(next(numbers))  # 0
print(list(numbers))  # [1, 4]

The explicit next consumes the first square. Converting the remaining iterator to a list does not repeat it. Generator expressions also defer work, while list comprehensions compute and retain results immediately.

Consumption and ownership

Operations that inspect an iterator can consume it:

Python
stream = iter(["header", "row-1", "row-2"])
same_stream = iter(stream)
print(same_stream is stream)  # True
has_header = any(line == "header" for line in same_stream)
print(has_header)  # True
print(list(stream))  # ['row-1', 'row-2']

iter(stream) returns the same cursor. any stops at its first match; without a match it would exhaust the stream. A downstream stage needs the remaining cursor, a replayable source, or an explicitly buffered copy.

Materialize a list when repeated access or a snapshot is required. State whether an API reads ahead, consumes everything, or leaves a remainder. Errors in generator bodies arrive during consumption rather than construction.

Gotchas

  • Laziness alone does not bound memory. Suspended generators retain locals and source iterators; sorting needs all items, and itertools.tee buffers when consumers progress at different rates.
  • Use return to finish a generator. Explicitly raising StopIteration from its body becomes RuntimeError.
  • Early loop exit does not guarantee prompt cleanup of a still-referenced generator. Keep resource ownership around consumption, or explicitly close a generator you own.

Put it into practice

Implement the exercise in a full-screen workspace, then run its tests.

Open practice
Next lessonClosures and decorators