🚀 Supercharge your YouTube channel's growth with AI.
Try YTGrowAI FreeGenerators in Python: How They Work
![Generators in Python [With Easy Examples]](/proxy?url=https%3A%2F%2Fwww.askpython.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fgenerators-in-python-featured-300x169.png)
Processing a sequence can mean handling each result as it arrives rather than saving every result in a list. A Python generator produces its next value when code asks for it.
I’ll show how to use a Python generator and when a generator expression fits a short calculation.
TL;DR
A generator produces values as code asks for them, so you can process a sequence without first building a list of every result.
- Put yield in a function to make it a generator function.
- Calling that function returns a one-pass generator object.
- Use a list when you need indexing or repeated access.
What Is a Generator in Python?
A generator is an iterator that produces its next value on demand because a function containing yield returns one value and suspends with its local state saved.
| Term | What it means |
|---|---|
| Iterable | An object that can supply an iterator through iter(), such as a list. |
| Iterator | An object that supplies values one at a time through next(). |
| Generator function | A function with yield. Calling it creates a generator object. |
| Generator object | The iterator that pauses and resumes the function as values are requested. |
A regular function stops at return. For a closer look at the iterator protocol, see the Python iterator guide.
How to Create and Use a Python Generator
Define a function with yield, then consume its object with a loop or a call to next. This complete example also uses a generator expression for a filtered calculation.
Step 1: Yield values and consume them
The return annotation Iterator[int] tells type-checking tools that the function yields integers. Python does not enforce that annotation at runtime. Save this as generator_walkthrough.py and run the command shown in the terminal screenshot.
from collections.abc import Iterator
def squares(limit: int) -> Iterator[int]:
for number in range(1, limit + 1):
yield number * number
values = squares(4)
print("First value:", next(values))
print("Remaining values:", list(values))
print("After exhaustion:", list(values))
print("Fallback after exhaustion:", next(values, "done"))
even_squares = (
number * number
for number in range(1, 5)
if number % 2 == 0
)
print("Even squares:", list(even_squares))
print("Sum of squares:", sum(number * number for number in range(1, 5)))
First value: 1
Remaining values: [4, 9, 16]
After exhaustion: []
Fallback after exhaustion: done
Even squares: [4, 16]
Sum of squares: 30

I ran this file for this refresh, and the output shows the first value followed by the remaining squares. The empty list confirms that the same object is exhausted. The expression yields even squares, while sum consumes its expression without building an intermediate list.
Step 2: Choose an expression for a short transform
The parenthesized expression in the file applies a calculation and a filter as iteration requests values. It suits a short pipeline, while a named function is clearer when the operation needs several statements or its own explanation. The call that builds a list above is for visible output, not a requirement for using an expression.
Common Generator Edge Cases
A generator suits a single-pass stream because the consumer can handle each result as it arrives. Choose a list when the result must support indexing, repeated passes or a stable snapshot.
| Situation | What happens | What to do |
|---|---|---|
| Values are needed once, one at a time | A loop can process each yielded item before requesting another. | Use a generator and avoid collecting every value. |
| The values need indexing or another pass | A generator is consumed and does not rewind itself. | Build a list, or call the generator function again for a fresh object. |
| The source can continue forever | A loop without a stopping condition never finishes. | Stop after the required count or condition. |
| The consumer calls list() | The values are materialized together at that point. | Use a streaming consumer if retaining all values defeats the goal. |
Calling next manually raises StopIteration after the final value, unless a default is supplied as in the example. A for loop handles normal exhaustion. Generators can lower the amount of retained output data, but they do not guarantee lower total memory or faster execution for every workload.
Review the list comprehension guide for a syntax comparison. The Python documentation on generator annotations covers send and return types.
Conclusion: Produce Values Only When Needed
Set a stopping condition when the source has no natural end. The Python yield reference above describes the function’s suspended state.
FAQ
These answers cover a couple of details that help when a generator becomes part of a function’s interface.
Can I use the same generator object twice?
No. Once it is exhausted, call the generator function again to create a fresh object, or store the values in a list if you need to reuse them.
What type annotation can I use for yielded integers?
Annotate the function with collections.abc.Iterator[int] when callers only need the iterator interface. Annotations help static tools and are not checked automatically at runtime.


