Generators and Iterators
Generators and iterators allow Python to produce values one at a time (lazy evaluation) instead of creating entire collections in memory.
This is critical when:
- Data is large (files, streams, APIs)
- Values are infinite or unknown
- You only need partial results
Iterables vs. Iterators
Section titled “Iterables vs. Iterators”Iterable
Section titled “Iterable”An iterable is any object that can be looped over.
Examples:
- list
- tuple
- string
- set
- dictionary
- file
nums = [1, 2, 3]for n in nums: print(n)Iterator
Section titled “Iterator”An iterator is an object that:
- Keeps track of position
- Produces next value on demand
Iterator Protocol
Section titled “Iterator Protocol”An object is an iterator if it implements:
__iter__()→ returns iterator object__next__()→ returns next value- Raises
StopIterationwhen done
graph TD
A[Iterable] --> B[iter]
B --> C[Iterator]
C --> D[next]
D --> E[Value]
C --> F[StopIteration]
Manual Iteration Example
Section titled “Manual Iteration Example”nums = [10, 20, 30]
it = iter(nums)
print(next(it)) # 10print(next(it)) # 20print(next(it)) # 30# next(it) -> StopIterationCustom Iterator
Section titled “Custom Iterator”class CountDown: def __init__(self, start): self.current = start
def __iter__(self): return self
def __next__(self): if self.current <= 0: raise StopIteration value = self.current self.current -= 1 return value
for num in CountDown(3): print(num)Generators
Section titled “Generators”Generators are a simpler way to create iterators.
Instead of writing a class, you use yield.
Key Idea
Section titled “Key Idea”return→ ends functionyield→ pauses function and saves state
Generator Function Example
Section titled “Generator Function Example”def generate_numbers(n): i = 1 while i <= n: yield i i += 1
gen = generate_numbers(3)
for num in gen: print(num)Execution Flow
Section titled “Execution Flow” graph TD
Call --> GeneratorObject
GeneratorObject --> StartExecution
StartExecution --> Yield
Yield --> Pause
Pause --> Resume
Resume --> Yield
Yield --> StopIteration
Important Behavior
Section titled “Important Behavior”- Function does NOT execute immediately
- Runs only when
next()is called - State is preserved between calls
Generators vs. Lists
Section titled “Generators vs. Lists”# List (memory heavy)nums = [x*x for x in range(1000000)]
# Generator (memory efficient)nums = (x*x for x in range(1000000))Generator:
- Computes values only when needed
- Uses constant memory
Generator Expressions
Section titled “Generator Expressions”Compact syntax similar to list comprehensions.
gen = (x * 2 for x in range(5))
for val in gen: print(val)Infinite Generators
Section titled “Infinite Generators”Generators can produce infinite sequences.
Example: Infinite Counter
Section titled “Example: Infinite Counter”def infinite_counter(): num = 1 while True: yield num num += 1
counter = infinite_counter()
for _ in range(5): print(next(counter))Output
Section titled “Output”12345Mental Model
Section titled “Mental Model” graph TD
Start --> Yield1
Yield1 --> Resume
Resume --> Yield2
Resume --> Yield3
Resume --> InfiniteLoop
Sending Values into a Generator
Section titled “Sending Values into a Generator”Generators can receive input using send().
Example
Section titled “Example”def accumulator(): total = 0 while True: value = yield total total += value
acc = accumulator()
print(next(acc)) # start generatorprint(acc.send(10)) # 10print(acc.send(5)) # 15Key Rule
Section titled “Key Rule”Closing a Generator
Section titled “Closing a Generator”When close() is called:
GeneratorExitis raised inside generator- You can clean up resources
Why we need to close generators?
Section titled “Why we need to close generators?”Example
Section titled “Example”def resource_manager(): print("Resource opened") count = 0
try: while True: yield count count += 1 except GeneratorExit: print(f"Cleaning up... used {count} times")
gen = resource_manager()
print(next(gen))print(next(gen))
gen.close()Output
Section titled “Output”Resource opened01Cleaning up... used 2 times graph TD
Start --> Yield
Yield --> Resume
Resume --> Yield
Close --> GeneratorExit
GeneratorExit --> Cleanup
Cleanup --> Stop
Interactive Generator Example
Section titled “Interactive Generator Example”def order_processor(): total_orders = 0 print("System Ready")
try: order = yield
while True: print(f"Processing: {order}") total_orders += 1 order = yield
except GeneratorExit: print(f"System Shutdown. Total orders: {total_orders}")
processor = order_processor()
next(processor) # start
processor.send("Pizza")processor.send("Burger")
processor.close()yield from Delegation
Section titled “yield from Delegation”Used to simplify nested generators
Without yield from
Section titled “Without yield from”def chain(): for x in [1, 2]: yield x for y in [3, 4]: yield yWith yield from
Section titled “With yield from”def chain(): yield from [1, 2] yield from [3, 4]