| name | use-generator-lazy |
| description | For memory efficiency: large sequences, infinite streams, early termination possible, pipeline processing without materializing all data. |
use-generator-lazy
When to Use
- Sequence is large or potentially infinite
- Only need one element at a time
- Early termination is common (find first match)
- Pipelining transformations
- Memory is constrained
When NOT to Use
- Need random access to elements
- Need length before iterating
- Will iterate multiple times
- Sequence is small
The Pattern
Use generator expressions and yield for lazy evaluation.
squares = (x**2 for x in range(1000000))
first_ten = list(islice(squares, 10))
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
from itertools import islice
first_20_fibs = list(islice(fibonacci(), 20))
def first_match(predicate, iterable):
for item in iterable:
if predicate(item):
return item
return None
Example (from pytudes)
def solve(formula):
"""Yield solutions as found - don't compute all at once."""
letters = all_letters(formula)
for digits in permutations('1234567890', len(letters)):
if valid(substitute(digits, letters, formula)):
yield substitute(digits, letters, formula)
first_solution = next(solve('SEND + MORE = MONEY'))
all_solutions = list(solve('SEND + MORE = MONEY'))
def life(world, n=float('inf')):
"""Yield n generations."""
for _ in range(n):
yield world
world = next_generation(world)
for generation, world in enumerate(life(glider, 100)):
display(world)
if is_stable(world):
break
def edits2(word):
"""All strings two edits from word."""
return (e2 for e1 in edits1(word)
e2 edits1(e1))
Key Principles
- Parentheses = lazy:
(x for x in ...) vs [x for x in ...]
- yield = generator function: Returns iterator, pauses between yields
- yield from = delegate:
yield from iterable yields each item
- next() for one: Get single item from generator
- Can't rewind: Once consumed, generator is exhausted