Generators and Iterators
Process unbounded data with bounded memory
Generators yield values lazily, so you can process data that does not fit in memory — the foundation of streaming code and chunked reads in every data tool.
What you will learn
- Write generators and generator expressions
- Explain laziness: nothing computes until iterated
- Chain generators instead of buffering intermediate lists
What is this?
A generator is a function with `yield` that produces values one at a time on iteration, instead of building a full list. Combined with iterators, they make 'process one record at a time' possible — the reason Python can read a 100 GB file or an infinite event stream.
Why do you need it?
- A list comprehension over a 10M-row file materializes 10M objects in memory; a generator expression streams them.
- Transforms in pandas/Spark and every streaming connector are implemented with lazy producers/consumers.
- Chaining lazy operations keeps memory flat regardless of input size.
Mental model
A tap vs a bucket
Generator vs list
Building a list fills the whole bucket before you drink; a generator turns the tap on and you sip as it flows. Both deliver water, only one scales to an infinitely large tank.
How it works
- `def gen(): yield 1; yield 2` suspends execution after each yield until the next `next()` call.
- Generator expressions `(x*2 for x in big)` are the lazy sibling of list comprehensions.
- Iteration drives a generator: each `for`/`next()` resumes after the last yield.
- Generators can be chained — a pipeline of small generators mirrors a DAG of transformations.
- `itertools` (islice, chain, groupby) composes with generators for production flows.
A lazy transformation chain.
Examples
Streaming a large file's first N valid rows
import itertoolsdef clean_records(path): with open(path) as f: for line in f: parts = line.strip().split(",") if len(parts) == 3: # valid yield partsfirst_10 = list(itertools.islice(clean_records("big.csv"), 10))The file is read lazily; islice stops pulling after 10 valid rows — gigabytes can be skipped without loading them.
In production
- A click-parser yields parsed events from a log stream rather than buffering a day's events.
- A chunked CSV reader splits a huge file into pandas DataFrames of N rows at a time for bounded-memory writes.
- An extractor wraps an API's pagination in a generator so callers iterate pages without knowing pagination exists.
Best practices
- Return early: a generator that must produce a list should collect and return, or callers re-iterate is impossible.
- Close underlying resources properly — generators opened inside `with` keep the file handle until exhausted.
- Use `itertools.islice(gen, n)` to bound consumption and 'peek' a few rows.
- Prefer generator expressions for single-pass filtering/mapping of large sequences.
Common mistakes
- Assuming generators can be re-iterated — they are single-use; `.list()` them if you need two passes.
- Opening a file inside a generator and never consuming it fully, leaking the handle.
- Wrapping a generator in a function that returns it but forgetting to call it — returning a generator object, not values.
Common issues
Generator produces nothing when I iterate twiceGenerators are exhausted and cannot be restarted
Re-create the generator (call the function again) or materialize with list() once if you need two passes.
Memory grows despite using a generatorAn intermediate list(...) or comprehension materializes the sequence
Convert to generator expressions or itertools so the chain stays lazy.
Debugging tips
- Confirm laziness: put a print in the generator body; nothing prints until you iterate.
- If a 'streaming' step spikes memory, check that an intermediate `list(...)` is not materializing the whole input.
- Trace a pipeline by iterating with a bounded islice and printing one record.
Interview checkpoint
Hands-on lab
- Generate a large CSV without loading it into memory
- Parse and filter it with a generator chain
- Prove memory stays flat and the output is correct
Stream a 10M-Row File Without Memory Blow-Up
25 min · intermediate
Quick quiz
What happens when you run `list(x * 2 for x in range(1000000))`?
A generator can be iterated multiple times like a list.