Python Generators and the yield Keyword, Explained
A generator is a function that produces values lazily, one at a time, using yield instead of return. Here's how Python generators work, why they save memory, and when to use them — with examples.
A generator is a Python function that produces values lazily — one at a time, on demand — using yield instead of return. Each yield hands back a value and pauses the function, preserving its state; the next request resumes right where it left off. The payoff is huge memory savings, because a generator never holds the whole sequence at once.
Generators are a Phase 5 “Pythonic” skill from the Python roadmap and a common interview topic — once you see how yield pauses and resumes, they click.
yield vs return
A normal function runs once and returns a single result. A generator function yields many values over time:
def count_up_to(n):
i = 1
while i <= n:
yield i # emit a value and PAUSE here
i += 1
for num in count_up_to(3):
print(num) # 1, then 2, then 3
Each time the loop asks for the next value, the function resumes after the yield, runs to the next yield, and pauses again. Its local variables (i) survive between pauses.
Why generators save memory
Compare building a million-item list versus generating them:
# List: builds all 1,000,000 in memory at once
nums = [i * i for i in range(1_000_000)]
# Generator: produces one at a time, ~constant memory
nums = (i * i for i in range(1_000_000)) # generator expression
The list allocates a million integers up front; the generator holds essentially one at a time. For large data, streaming, or infinite sequences, that’s the difference between fitting in memory and crashing.
Generator expressions
Like list comprehensions but with parentheses, producing a lazy generator instead of a full list:
total = sum(x * x for x in range(1000)) # no intermediate list
When you only need to consume a sequence once (like feeding sum), a generator expression is both faster to start and lighter on memory.
When to use a generator
- Large datasets — process a huge file line by line without loading it all.
- Infinite or open-ended sequences —
yieldforever, take what you need. - Streaming / pipelines — chain generators to transform data lazily.
- One-pass iteration — when you’ll loop once and don’t need the list again.
A practical example: reading a big file
def read_lines(path):
with open(path) as f:
for line in f:
yield line.strip() # one line at a time
for line in read_lines("huge.log"): # never loads the whole file
process(line)
This processes a multi-gigabyte file in near-constant memory — a textbook generator use.
Common mistakes
- Expecting to reuse a generator — it’s exhausted after one full iteration. Create a new one to iterate again.
- Calling
len()on a generator — it has no length; convert to a list first if you truly need it (losing the memory benefit). - Indexing a generator —
gen[0]doesn’t work; generators don’t support random access. - Forgetting it’s lazy — nothing runs until you iterate; bugs can hide until the first consumption.
Where this fits
Generators are part of Phase 5 of the Python roadmap, alongside decorators, and they share the lazy, expression-based style of comprehensions.
They’re drawn out with diagrams in the job-ready Python in Three Months, with the basics introduced in Python in One Month. For coroutines, yield from, async generators, and building data pipelines, Python for Staff Engineers goes deeper.
Think “produce on demand, remember your place” and generators become a natural tool for big or streaming data.
Frequently asked questions
What is a generator in Python?
A generator is a function that produces a sequence of values lazily, one at a time, instead of building and returning them all at once. It uses the yield keyword to emit a value and pause, resuming where it left off on the next request. This makes generators memory-efficient for large or infinite sequences.
What does the yield keyword do in Python?
yield emits a value from a generator and pauses the function, preserving its local state. The next time the generator is asked for a value, it resumes right after the yield. Unlike return, which ends a function, yield lets it produce many values over time without losing its place.
What is the difference between yield and return?
return ends a function and hands back a single result. yield produces a value and suspends the function so it can continue later, producing more values. A function with return runs once to completion; a function with yield becomes a generator that can be iterated over, yielding values on demand.
When should I use a generator instead of a list?
Use a generator when the sequence is large, infinite, or streamed, or when you only need to iterate once — it produces values on demand and uses almost no memory. Use a list when you need random access, the length, or to iterate multiple times, since a generator is exhausted after one pass.