Function calls
standardintermediateCalling f(args) creates a new frame, binds each argument to its parameter name inside that frame, runs the function body, and destroys the frame when it returns — handing the return value back to the caller.
Think of it as
Calling a function is checking out a fresh whiteboard just for that call, writing the argument values on it under the parameter names, working through the function body using only what is on that board (plus anything visible from outside), and wiping the board the moment the function returns — after copying the final answer onto the caller's own board first.
What we're doing: Show that every call to the same function gets its own independent frame, even for recursive calls, by inspecting each frame's local namespace.
- 4
- sys._getframe() returns THIS call's own frame object.
- 6
- countdown(n - 1) is a new call — even though it is the same function, it gets a completely separate frame.
n=2, this frame's id=...
n=1, this frame's id=...
n=0, this frame's id=...Why this works: Each call to countdown(n) — even recursive ones calling the exact same function object — creates its own fresh frame with its own local namespace, so n=2's frame is a completely different object from n=1's frame, each with its own independent value for n. This is why recursion works at all: if calls shared a frame, every recursive call would stomp on the same n instead of each having its own.
Assuming a default argument is re-evaluated on every call
Wrong
Better
What you see: append_item('b') prints ['a', 'b'], not ['b'] — the 'fresh' default list from the first call is still there on the second.
Why: A default argument value is evaluated exactly once, when the def statement itself runs — not once per call. items=[] creates ONE list object that every call without an explicit items argument reuses and mutates. This is a fact about how def builds the function object, not about how each individual call binds its frame — each call's frame is still fresh, but a mutable default's shared reference is bound into that fresh frame every time.
What a call to f(a, b) does, step by step
Together
Remember: Every call to f(args) creates a brand-new frame, binds arguments inside it, runs the body, then discards the frame. Recursive calls never share a frame.
See also: call stack and frames · local and global namespaces · defining functions

