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.
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:
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.teebuffers when consumers progress at different rates. - Use
returnto finish a generator. Explicitly raisingStopIterationfrom its body becomesRuntimeError. - 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.