CodeRigor

FREE LESSON

Iterators, generators & streaming batches

Single-pass iterables, lazy generators and bounded-memory batches for datasets larger than memory.

An iterable can produce an iterator; an iterator remembers its current position. next advances it and exhaustion raises StopIteration. A list can be iterated again, but a generator is ordinarily a single-pass stream.

A generator advances only when the consumer requests another result.

Streaming one batch at a time
start / 3
At size two, the first next() consumes only records 0 and 1. The producer does not first materialize the whole source.
Streaming one batch at a time
At size two, the first next() consumes only records 0 and 1. The producer does not first materialize the whole source.
Python
source = (x * x for x in range(3))
print(next(source))
print(list(source))
print(list(source))  # exhausted

A function containing yield creates a generator. Its body runs on demand, suspends at each yield, and continues on the next request. Returning a list first materializes everything; this can exhaust memory on a large dataset or hang on an infinite source. Do not make a separate counting pass over a stream.

Python
def take_even(source):
    for x in source:
        if x % 2 == 0:
            yield x


it = take_even(iter([1, 2, 3, 4]))
print(next(it))
print(list(it))

A batching generator holds at most one chunk. After yielding a list, create a new list instead of clearing and reusing the old one: consumers may keep references to earlier chunks. The last partial chunk must survive exhaustion.

Rebinding the producer buffer keeps earlier yielded batches intact; clearing it mutates objects the consumer retained.

Yielded lists need separate storage
The consumer keeps batch A while the producer fills batch B. These must be different lists.
Yielded lists need separate storage
The consumer keeps batch A while the producer fills batch B. These must be different lists.

The build checks laziness with a source that records every requested element. Creating the batching iterator must not consume records; asking for the first chunk consumes exactly that chunk. Invalid size may raise when the generator is first advanced, because its body has not run before then. Empty sources yield no chunks. Test an infinite counter using only the first two batches.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Watch how yield defers work until iteration. Trace when the source advances in the lesson’s lazy batches, and check that yielded batches do not share a mutable buffer.