Python Functions, Recursion & Advanced
Python Functions Interview Questions
CPython lacks tail-call elimination—recursive style still grows the call stack; rewrite iteratively or use trampoline/explicit stack for deep cases.
RecursionError: maximum recursion depth exceeded—often fix by iteration, sys.setrecursionlimit is risky band-aid.
Naive recursion recomputes the same subproblems—time grows exponentially. Memoization or bottom-up DP fixes that.
# Naive (slow): overlapping work
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
# Memoized (typical fix)
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)Same O(log n) stack in theory—but iterative avoids stack frames; recursion chosen for clarity on small trees, not deep arrays in Python.
Alternating calls still consume frames—depth sums across the chain; base cases required in both functions.
Prevent revisiting nodes—without it, cycles cause infinite recursion even if tree recursion pattern looks correct.
Arguments must be hashable—mutable default args or dict params break caching; memory grows with distinct argument tuples.
O(n log n)—mergesort-style; interviews verify you connect recurrence to balanced splitting.
O(n) frames if pivot always smallest/largest—same skew hurts recursion depth; randomized pivot or introspective sort mitigates.
Yes—tail-recursive shape in FP but not optimized in Python; pattern clarifies state threading vs global mutation.
Delegates iteration to subgenerator—flattens tree walks without building intermediate lists; recursion depth still applies to generator frames.
Propagates up callers—finally runs per frame during unwind; resource cleanup still matters in recursive try blocks.
Call stack is implicit stack—explicit deque/list stack simulates same traversal order with controlled depth.
Grows insanely fast—shows recursion can explode even with tiny-looking arguments; not practical to run naively.
Inorder yields sorted order for valid BST—recursive structure maps cleanly but watch stack on skinny trees.
Manual dict allows domain pruning and custom eviction—lru convenient with bounded memory via maxsize.
Need explicit depth limit or base resolution—floating drift causes infinite aesthetic recursion bugs.
Production systems avoid stack limits and overhead—e.g. explicit queues for BFS level-order vs naive deep recursion.
Deep copy semantics matter—shallow copy duplicates nodes but child references may alias unless reconstructing fresh nodes.
Stack traces bounce between functions—unit tests need scenario covering full cycle and base cases in each hop.
Use object references as nodes—same depth limits; iterative cursor loop often simpler.
Recursive descent maps grammar rules to functions—left recursion grammars break naive recursion; refactor grammar or use iterative parser.
No—stack overflow is frame exhaustion, not memory leaks from objects; unrelated mechanisms.
Fractals, tree/graph definitions, divide-and-conquer clarity—sometimes readability wins until profiling demands iteration.
Turns deep recursion into loop bouncing thunks—advanced FP technique to flatten stack growth in Python.
Concurrent or re-entrant calls corrupt shared counters—pass accumulators or encapsulate in objects.
O(n) stack frames for substring slicing naive—two-pointer iterative O(1) extra space.
Naive backtracking retries characters—KMP uses LPS array to skip—recursion isn’t the efficient tool for production search.
If nested lists can reference parents (cycles), naive recursion loops forever—track seen container ids.
Expect explicit stack/queue, sliding window, or loop invariants—demonstrate equivalence and complexity rigorously.