Iterators & Generators
Iterators & Generators
Section titled “Iterators & Generators”Iterables vs Iterators
Section titled “Iterables vs Iterators”- An iterable is any object you can loop over (
list,str,dict, etc.). - An iterator is an object that implements
__iter__()and__next__().
nums = [1, 2, 3]it = iter(nums) # get iterator from iterable
print(next(it)) # 1print(next(it)) # 2print(next(it)) # 3# 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 val = self.current self.current -= 1 return val
for n in Countdown(3): print(n) # 3, 2, 1Generators with yield
Section titled “Generators with yield”A generator function uses yield to produce values lazily — one at a time, on demand:
def countdown(n): while n > 0: yield n n -= 1
for val in countdown(3): print(val) # 3, 2, 1Generators are memory-efficient — they don’t build the full sequence in memory.
Generator State
Section titled “Generator State”A generator pauses at each yield and resumes from where it left off:
def gen(): print("start") yield 1 print("middle") yield 2 print("end")
g = gen()next(g) # prints "start", returns 1next(g) # prints "middle", returns 2next(g) # prints "end", raises StopIterationyield from
Section titled “yield from”Delegates to a sub-generator:
def chain(*iterables): for it in iterables: yield from it
list(chain([1, 2], [3, 4])) # [1, 2, 3, 4]Generator Expressions
Section titled “Generator Expressions”Like list comprehensions but lazy:
# List comprehension — builds full list in memorysquares_list = [x**2 for x in range(1000000)]
# Generator expression — lazy, memory-efficientsquares_gen = (x**2 for x in range(1000000))
print(next(squares_gen)) # 0print(sum(squares_gen)) # compute on the flySending Values into a Generator
Section titled “Sending Values into a Generator”def accumulator(): total = 0 while True: value = yield total if value is None: break total += value
acc = accumulator()next(acc) # prime the generatoracc.send(10) # 10acc.send(20) # 30acc.send(5) # 35Practical Use Cases
Section titled “Practical Use Cases”# Reading large files line by linedef read_large_file(path): with open(path) as f: for line in f: yield line.strip()
# Infinite sequencedef integers(start=0): n = start while True: yield n n += 1
from itertools import islicefirst_10 = list(islice(integers(), 10))itertools Highlights
Section titled “itertools Highlights”import itertools
list(itertools.count(1, 2)) # 1, 3, 5, 7, ... (infinite)list(itertools.cycle([1, 2, 3])) # 1, 2, 3, 1, 2, 3, ... (infinite)list(itertools.repeat(5, 3)) # [5, 5, 5]list(itertools.chain([1,2],[3,4])) # [1, 2, 3, 4]list(itertools.islice(range(100), 5)) # [0, 1, 2, 3, 4]