Generator Patterns
Common Generator Design Patterns
1. Pipeline Pattern
Generators can be chained to create data processing pipelines:
def read_large_file(file_path):
with open(file_path, 'r') as file:
for line in file:
yield line.strip()
def filter_data(lines):
for line in lines:
if line and not line.startswith('#'):
yield line
def process_data(filtered_lines):
for line in filtered_lines:
yield line.upper()
## Chaining generators
file_path = '/tmp/sample_data.txt'
pipeline = process_data(filter_data(read_large_file(file_path)))
Generator Composition Patterns
graph LR
A[Input Generator] --> B[Filter Generator]
B --> C[Transformation Generator]
C --> D[Output]
2. Infinite Sequence Generators
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
## Using infinite generator
fib_gen = fibonacci()
fib_sequence = [next(fib_gen) for _ in range(10)]
print(fib_sequence)
Generator Patterns Comparison
| Pattern |
Use Case |
Memory Efficiency |
Complexity |
| Pipeline |
Data Processing |
High |
Medium |
| Infinite Sequence |
Mathematical Sequences |
Very High |
Low |
| Stateful Generator |
Complex Iterations |
Medium |
High |
3. Coroutine-like Generators
def coroutine_generator():
while True:
x = yield
print(f"Received: {x}")
## Coroutine usage
coro = coroutine_generator()
next(coro) ## Prime the coroutine
coro.send(10)
coro.send(20)
Advanced Generator Techniques
Generator Delegation
def sub_generator():
yield 1
yield 2
def main_generator():
yield 'start'
yield from sub_generator()
yield 'end'
result = list(main_generator())
print(result) ## ['start', 1, 2, 'end']
Practical Applications
At LabEx, we've found generators particularly useful in:
- Large dataset processing
- Stream processing
- Memory-efficient data transformations
- Implementing custom iteration logic
def memory_efficient_range(start, end):
current = start
while current < end:
yield current
current += 1
## Compare memory usage with list
import sys
list_range = list(range(1000000))
gen_range = memory_efficient_range(0, 1000000)
print(f"List memory: {sys.getsizeof(list_range)} bytes")
print(f"Generator memory: {sys.getsizeof(gen_range)} bytes")
Best Practices
- Use generators for large or infinite sequences
- Prefer generator expressions for simple transformations
- Be cautious of multiple iterations
- Understand the one-time nature of generators
By mastering these patterns, you'll unlock the full potential of generators in Python, creating more efficient and elegant code solutions.