Call Stack —
The foundation everything else on this page builds on.
Every function call — recursive or not — pushes a frame onto the call stack, and every return pops one off, last in first out. Recursion just means a function's frame can end up waiting on another frame of the same function. Watch three plain function calls push and pop on LearnBug before touching anything recursive at all.
What is the Call Stack?
A region of memory that tracks every function call currently in progress. Each call gets its own "frame" holding its local variables and exactly where execution should resume once whatever it's waiting on returns. Calling a function pushes a new frame on top; returning from one pops it off — always the most recently pushed frame first.
Why visualization helps
"LIFO" is easy to say and hard to feel without watching it. LearnBug shows the stack as an actual growing/shrinking list of frames, so when a() calls b() calls c(), you can see that c's frame sits on top and must finish completely before b's frame can resume — no shortcuts, no skipping.
What every frame remembers
A frame per call
Every single function call gets its own frame — even calling the same function twice creates two separate, independent frames, each with its own local variables.
The top of the stack runs first
Only the topmost frame is actively executing. Every frame beneath it is paused, waiting at the exact line where it made its call, for that call to return.
Too many frames → RecursionError
Python caps the stack at roughly 1000 frames. A recursive function with no working base case grows the stack forever and eventually blows past that limit.
a() calls b() calls c() — no recursion, just the stack itself
a() starts, prints "a: start", then calls b() — a's frame pauses
a() doesn't finish running — it's paused exactly at the line that calls b(), waiting for b() to return before it can reach "a: end".
b() starts, prints "b: start", then calls c() — b's frame pauses too
Now two frames are paused, stacked on top of each other. b() is waiting on c() the same way a() is waiting on b().
c() runs start-to-finish with nothing left to call — pops immediately
c() prints both its lines and returns without pausing, since it never calls anything else. Its frame is removed — the stack shrinks back to two.
b() resumes exactly where it paused, prints "b: end", pops — then a() does the same
Each frame resumes in strict reverse order of how it was pushed: b finishes and pops, then a resumes and pops last. The stack ends empty in the exact reverse order it filled.
Final output order: a:start, b:start, c:start, c:end, b:end, a:end ✓ LIFO, visibly
Three plain calls, no recursion
def a():
print("a: start")
b() # a pauses here until b fully returns
print("a: end")
def b():
print("b: start")
c() # b pauses here until c fully returns
print("b: end")
def c():
print("c: start")
print("c: end") # c calls nothing — never pauses, returns straight away
a()Complexity Notes
Frequently asked questions
Is the call stack only used for recursion?
No — every single function call in every program uses the call stack, recursive or not. The a/b/c example above has zero recursion and still demonstrates the exact same push/pop mechanics that recursive functions rely on. Recursion is just the case where a function's frame ends up waiting on another frame running the same function.
What exactly does "popping a frame" mean?
When a function finishes — hits a return or runs off the end of its body — its frame is removed from the top of the stack, and control resumes in whatever frame is now on top, exactly at the line after the call that created the popped frame.
What causes a RecursionError specifically?
Frames get pushed faster than they get popped — usually because a recursive function's base case is missing, wrong, or unreachable for the given input, so calls keep nesting deeper with nothing ever returning to pop them off, until Python's frame limit is exceeded. See Base Case vs Recursive Case for a concrete broken example.
Why do local variables inside a() not get overwritten by b()'s locals?
Because each frame has its own completely separate storage for its local variables. a()'s frame and b()'s frame coexist on the stack at the same time but never share memory — that's exactly what "local scope" means, and it's why two active calls to the same function (as in recursion) don't clobber each other's variables.
Watch every frame push and pop, in order
Paste any function calls into LearnBug and see the call stack build and shrink live.