Python Generators
What is a Generator?
A generator is a special type of iterator — created using a function with the yield keyword instead of return. It was introduced to make creating iterators simpler and more memory-efficient.
| Iterator (legacy) | Generator | |
|---|---|---|
| Created with | A class with __iter__ and __next__ |
A function with yield |
next() support |
✅ | ✅ |
StopIteration |
Must raise manually | Raised automatically |
| Complexity | More boilerplate | Much simpler |
The yield Keyword
- Like
return, it sends a value back to the caller - Unlike
return, it pauses the function and preserves its internal state - When
next()is called again, execution resumes from where it left off
def gen():
yield 1
yield 2
yield 3
itr = gen() # returns a generator object (not the result)
print(type(gen)) # <class 'function'>
print(type(itr)) # <class 'generator'>
print(next(itr)) # 1
print(next(itr)) # 2
print(next(itr)) # 3
print(next(itr)) # ❌ StopIteration
Execution flow:
1. First next() → runs until yield 1, pauses, returns 1
2. Second next() → resumes, runs until yield 2, pauses, returns 2
3. Third next() → resumes, runs until yield 3, pauses, returns 3
4. Fourth next() → no more yield → raises StopIteration
Looping Through a Generator
Since a generator is an iterator, you can use it in a for loop:
for i in gen():
print(i) # 1 2 3
Practical Example — Fibonacci Sequence
Traditional approach (stores everything in a list ⚠️)
fib_numbers = [1, 1]
for i in range(2, 10):
last = fib_numbers[i - 1]
second_last = fib_numbers[i - 2]
fib_numbers.append(last + second_last)
print(fib_numbers)
Problem: stores every number in memory. For 10 million terms, the list becomes huge.
Generator approach (stores only what's needed ✅)
def fib(n):
last = 1
second_last = 1
current = 3 # first two terms (1, 1) are already known
while current <= n:
num = last + second_last
yield num
second_last = last
last = num
current += 1
for val in fib(10):
print(val) # 2 3 5 8 13 21 34 ...
Only two variables (last, second_last) are stored at any time — not the whole sequence.
Step-by-step for the first two iterations:
| Step | last | second_last | num yielded |
|---|---|---|---|
| current=3 | 1 | 1 | 2 |
| current=4 | 2 | 1 | 3 |
| current=5 | 3 | 2 | 5 |
Why Use a Generator?
| Use a generator when | Use a list when |
|---|---|
| You only need one value at a time | You need random access to any value |
| The sequence is infinite or very large | The sequence is small and finite |
| Memory efficiency matters | You need all values available simultaneously |
| Values are generated on demand | All values are needed at once |
Key insight: A generator doesn't store the whole sequence — it computes the next value on demand. This makes it ideal for infinite or very large sequences.
Key Takeaways & Recap
| Concept | Summary |
|---|---|
| Generator | A function with yield — returns a generator object when called |
yield |
Pauses execution and returns a value; resumes on next next() call |
StopIteration |
Raised automatically when the generator is exhausted |
| Memory efficient | Only current state is stored — not all values |
| Iterable | Can be used in for loops just like any iterator |
| When to use | Infinite sequences, large datasets, one-value-at-a-time access |