Control Flow: Traps, Failure Modes & Interview Q&A
Master iteration failure modes, recursion stack limits, generator memory lifecycle pitfalls, decision criteria, and senior interview questions.
Navigating control flow, iterators, and coroutines at scale requires a firm grasp of their operational limits, resource lifecycles, and subtle failure modes.
This lesson explores practical edge cases, explains why CPython omits tail-call optimization, provides a data pipeline decision matrix, and reviews key technical interview flashcards.
Configured via sys.getrecursionlimit() to guard against native C stack overflow crashes.
1. Edge Cases & Architectural Failure Modes#
Trap 1: Recursion Limit & The Lack of Tail-Call Optimization#
Unlike functional languages (such as Scheme or Haskell), Python does not support Tail-Call Optimization (TCO). Every recursive call pushes a new PyFrameObject and consumes native C stack space:
12345678910111213import sys print(f"Current recursion limit: {sys.getrecursionlimit()}") def recursive_factorial(n: int) -> int: if n <= 1: return 1 return n * recursive_factorial(n - 1) try: recursive_factorial(2000) except RecursionError as e: print(f"Caught expected error: {e}")
Trap 2: Generator Exhaustion#
Iterators and generators are strictly single-pass data streams. Once exhausted, they cannot be rewound or restarted:
1234567891011121314def number_stream(): yield 1 yield 2 yield 3 stream = number_stream() # First pass: Consumes the stream completely first_pass = list(stream) print(f"First pass : {first_pass}") # [1, 2, 3] # Second pass: Generator is already exhausted! second_pass = list(stream) print(f"Second pass: {second_pass}") # [] (Empty list!)
2. Data Pipeline Design Decision Matrix#
| Workload / Use Case | Architecture Selection | Memory Profile | Performance Characteristics |
|---|---|---|---|
| Huge files / unbounded data streams | Generator functions (yield) | constant memory | Lazy pull streaming; minimal RAM footprint. |
| Small in-memory transformations | List / Set / Dict Comprehensions | memory | Fast C loop execution; optimized LIST_APPEND opcodes. |
| Declarative multi-shape parsing | Structural Pattern Matching (match/case) | auxiliary | Fast jump-table matching with zero regex parsing overhead. |
| Stateful event consumer / processor | Coroutine Generators (send) | constant memory | Cooperative push pipeline; decoupled from driver loops. |
| Intensive numerical array processing | NumPy / Polars arrays | Compact C contiguous buffers | SIMD vectorization, cache locality, and zero PyObject overhead. |
3. Senior Technical Interview Flashcards#
4. Concept Verification#
Test your understanding of iteration trade-offs and control flow limits:
Why does CPython deliberately omit tail-call optimization (TCO) in recursive functions?
What happens when you iterate over an already-exhausted generator a second time?