Unit 3.4 · about 4 h
13 / 324Iterators, generators and lazy pipelines
Data work streams. Single-pass exhaustion and silent re-iteration bugs are the usual pipeline failures.
- explain
- implement
You can now
- Implement a reusable iterator with tests for exhaustion, repeated iteration and invalid input.
- Implement a lazy generator pipeline and compare it with the eager version on peak memory, time and correctness.
- Predict generator cleanup on
close,throwand deallocation, and PEP 479StopIterationhandling.
Covers
- Iterable and iterator protocols, exhaustion and repeated iteration
- Generators, lazy pipelines, send and cleanup behavior
Study, in this order
5 steps
- Comprehension forms and itertoolsStas' Python Cookbook - selected path
Comprehensions, Iterators and itertools:
list/dict/set/generatorforms; the essential itertools (chain,islice,groupbyon sorted input);zipstrict=Trueandzip_longest - Generators and yieldStas' Python Cookbook - selected path
Generators and
yield: theread_bigline pipeline, infinite integers,yield frominflatten() - Generator functions and container protocolsPython language reference
Section 3.2.8.3 Generator functions (calling returns an iterator; the body runs on first
next) and 3.3.7 Emulating container types (__len__,__iter__,__getitem__,__contains__) - Custom IteratorsAlgoMaster Python - structured revision
The Custom Iterators lesson and its exercise; write
__iter__and__next__from memory first - GeneratorsAlgoMaster Python - structured revision
The Generators lesson and Exercise: Generators; then the
yieldStatement lesson forsendandreturnvalues
Focus on
- An iterable makes fresh iterators; an iterator is consumed once.
iter()on alistgives a new object each time, while a generator is its own single-pass iterator. - Generators suspend at
yieldand resume after it.send()delivers a value to the pausedyieldexpression; areturnvalue becomesStopIteration.value;close()raisesGeneratorExitinside. - Raising
StopIterationinside a generator body is aRuntimeErrorsince Python 3.7 by PEP 479; usereturnto stop. - Lazy wins memory only when nothing materializes. One
list()call in the middle removes the gain; measure peak withtracemallocon the same workload.
Reproduce closed-book
Range iterable and a single-pass variant; assert the second list() pass is full for the first and empty for the second. Then run the eager versus lazy comparison: list of 1M squares peaks near 40 MB while the genexpr sum peaks under 1 KiB with equal results, measured with tracemalloc on CPython 3.14.4 default build, 12th Gen Intel Core i9-12900K.Traps
- Re-iterating an exhausted generator and expecting fresh data. The second pass is empty with no error.
- An accidental
StopIterationinside a generator body. It surfaces asRuntimeError, not a quiet stop. itertools.groupbyacross unsorted input. It groups consecutive items only; sort first or accept split groups.
Practice
AlgoMaster Python: Exercise: Custom Iterators
Exercises 1 to 5, especially Exercise 2: Reusable Product Catalog Iterator and Exercise 5: Independent Iterators That Skip Closed Tickets
Implement __iter__ and __next__ in your own file before checking.
AlgoMaster Python: Exercise: Generators
Exercises 1 to 5, especially Exercise 3: Consume a Generator Once and Exercise 5: Flatten Nested Lists Lazily
Add exhaustion and second-pass assertions the exercise does not ask for.
Self-test
-
How do you test whether an object is an iterable or an iterator?
Calliter()twice: alistgives two distinct working iterators, while a generator returns itself.list()on an exhausted generator is[]. -
A generator yields 1 then raises
StopIteration('x'). What does the caller see?RuntimeErrorby PEP 479, notStopIteration. End a generator withreturn, optionally with a value. -
How does a caller read a value returned by a generator, and how does it send one in?
return 'done'surfaces asStopIteration.valueon exhaustion.send(v)resumes the pausedyieldexpression withv; prime withnext()first. -
The lazy pipeline uses the same peak memory as the eager one. What is the first suspect?
Something materializes: alist(),sorted(),len()or reuse of the data downstream. Find the allocating line withtracemalloc.
Completion criterion
Stop when
Your iterator passes exhaustion, repeat and invalid-input tests, and your pipeline matches the memory numbers with equal outputs.
Mark passed, then open 3.5 Only mark this when you can do it without notes.Leads to
- 3.6 Context managers, exceptions and deterministic cleanup Pair generator cleanup with deterministic closing.
- 5.2 Property, metamorphic and differential testing with shrinking State iterator invariants as properties: exhaustion, repeatability, input rejection.
Comes after
- 3.3 Attribute lookup, object protocols and dataclassesThe iteration protocol in depth:
__iter__,__next__and exhaustion. - 3.9 Reference counts, cycle collection and weak referencesLazy pipelines and weak caches both bound memory; compare the two tools.
Resources for this unit
- Stas' Python Cookbook - selected path Run Part I on dictionaries, iteration, functions, closures, decorators, classes and context managers; Part II on modules, files, paths, environment and arguments; and Part IV on versions, dependencies, packaging and code quality. Finish by building one src-layout package from a blank directory.
- Python language reference Check Data model, Execution model, Import system and Exceptions whenever a claim depends on Python semantics. These pages are the authority when the course and a runtime observation disagree.
- AlgoMaster Python - structured revision Use Functions, Modules and Packages, OOP, Advanced OOP, Exception Handling, Iterators and Generators, Type Hints and Annotations only after the cookbook pass. Open it for recall practice or a diagnosed weak mechanism, not as a second complete course.
Also in this section
- Packaging Python Projects Follow the packaging tutorial once from a blank directory, using a src layout, pyproject.toml, an isolated build and a clean-environment installation test.
- dataclasses - data classes Read the
@dataclassparameters (eq,order,frozen,slots),field()withdefault_factory, frozen instances, post-init processing,replace,asdictandastuple. Use for the validated frozen dataclass in attributes-protocols-dataclasses. - typing - type hints and protocols Read Protocols for structural typing and
runtime_checkablelimits, Generics forTypeVargenerics, andassert_typefor narrowing checks. Use for theProtocoland narrowing parts of type-hints-protocols; PEP 695 syntax itself is in the language reference. - gc - cycle garbage collector Read the cycle collector interface:
collect,get_count,garbageanddisable/enable. Use for the cycle-collection and measurement parts of refcounting-gc-weakrefs. - weakref - weak references Read weak-reference objects (
refcallbacks,proxyobjects) and theWeakValueDictionaryexample. Use for the weak-reference part of refcounting-gc-weakrefs.