Return Values and Passing Data Between Functions
Nested function calls occur when one function invokes another during its execution, causing the first function to pause while the second runs.
The Waiting Chain
A function does not always run alone. During its execution, it may call another function. When that happens, the first function pauses while the second function runs. If the second function calls a third function, the second function pauses as well. The result is a chain in which each calling function waits for the function it invoked to finish.
Think of a conversation in which one person asks another person a question. The second person needs to ask a third person before answering. The first person waits, the second person waits, and the third person's answer allows the second person to continue. The second person can then answer the first person. Nested function calls follow the same waiting pattern.
What do you think happens?
Suppose greet_user calls get_name, and get_name calls ask_question. Which function is running when ask_question is active?
Reveal answer
Answer: ask_question, while greet_user and get_name are waiting
A nested call pauses the calling function while the called function runs. Therefore, greet_user and get_name remain active but paused while ask_question executes.
Growing the Call Stack
Python uses a call stack to track active functions and their execution order. Each function call adds a frame to the top of the stack. The frame represents an active function and preserves information Python needs while that function is paused or running. The most recently called function is at the top, so it must finish before execution can return to the function below it.
The stack grows one frame at a time. First, main is active. When main calls greet_user, greet_user is added above it. When greet_user calls get_name, get_name is added above greet_user. When get_name calls ask_question, ask_question becomes the top frame. At that moment, all four functions are represented on the stack, but only ask_question is currently executing.
Following Execution Order
Nested calls follow a strict order. A call transfers control downward to the new function. The called function runs until it finishes or reaches a return statement. Control then moves upward to the function that made the call. This means Python cannot skip an active call or return to a function that has not yet been called.
- main starts
- main calls function_a
- function_a starts
- function_a calls function_b
- function_b starts
- function_b calls function_c
- function_c starts
- function_c finishes and returns
- function_b resumes and finishes
- function_a resumes and finishes
- main resumes and finishes
The important pattern is depth first: execution follows a call until the deepest active function finishes, then returns through the waiting functions in reverse order. The function that called function_c resumes before function_a can resume, and function_a resumes before main can resume.
Returning Data and Resuming Work
When a function returns a value, that value travels back to the function that called it. Python removes the completed function's frame from the call stack and resumes the caller at the exact line where the call occurred. The caller can then use the returned value as it continues its own work.
Tracing greet_user, get_name, and ask_question
Follow the call and return sequence when greet_user calls get_name, and get_name calls ask_question.
Start greet_user: greet_user begins running. When it encounters the call to get_name, greet_user pauses and remains active on the call stack.
Run get_name: get_name starts while greet_user waits. When get_name encounters the call to ask_question, get_name pauses and ask_question begins.
Return from ask_question: ask_question finishes and returns a value. Python removes ask_question from the top of the stack and resumes get_name at the point immediately after the call.
Return from get_name: After using or processing the result from ask_question, get_name finishes and returns a value. Python removes get_name and resumes greet_user where the call to get_name occurred.
Continue greet_user: greet_user can now use the value returned by get_name and continue its remaining work.
The return path reverses the call path: ask_question returns to get_name, then get_name returns to greet_user.
Stack Unwinding and Local Scope
The call stack does not only grow; it also shrinks. When the top function finishes, Python removes its frame first. Execution then resumes in the frame below it. This last-in-first-out behavior means the most recently added function must finish before earlier callers can continue.
Each function has its own local scope. Local variables exist only while that function is on the call stack. When the function returns and its frame is removed, those local variables are destroyed. If the same function is called again later, the new call receives a fresh set of local variables.
If function_b has a local variable named z, z belongs to that active call of function_b. When function_b returns, z is destroyed with that function's frame. A later call to function_b creates a new local z rather than reusing the previous call's local variable.
Reading the Traceback
The call stack is useful when debugging. If a program crashes with an error, Python shows a traceback, which is a printout of the call stack at the moment of the error. The traceback identifies the active functions and the order in which they were called. Reading that order helps you determine how execution reached the failing point.
- Find the function where the error was reported.
- Read the surrounding call entries to identify which function called it.
- Continue upward through the traceback to reconstruct the nested call path.
- Use the call path to determine which function was waiting for the failing function's result.
Mistakes in Nested Calls
Thinking that the caller continues running while the called function runs
A nested call pauses the calling function while the called function runs.
Fix:
Place the caller in a waiting state and follow the called function until it finishes.Following returns in the same order as calls
The call stack is last in, first out. The most recent caller must finish before earlier callers resume.
Fix:
Reverse the call path when tracing returns: function_c returns to function_b, then function_b returns to function_a, then function_a returns to main.Expecting local variables to remain available after a function returns
Local variables exist only while their function is on the call stack and are destroyed when the function returns.
Fix:
Pass needed data back through a return value or otherwise use data that is available in the appropriate scope.Ignoring the exact resume point
Python resumes the caller at the exact line where the nested call occurred.
Fix:
Mark the call location before entering the nested function, then continue immediately after that location when the function returns.
Practice the Trace
Trace this call chain in words: main calls function_a, function_a calls function_b, and function_b calls function_c. Identify the order in which functions start, the order in which they finish, and the function that resumes after each return.
Hints
- Write the calls from the outermost function toward the deepest function.
- The deepest active function finishes first.
- For each return, move one level back toward main.
Practice Answer
Determine the execution order for main calling function_a, function_a calling function_b, and function_b calling function_c.
Calls move downward: main starts, then calls function_a. function_a starts, then calls function_b. function_b starts, then calls function_c.
The deepest function finishes first: function_c finishes and returns control to function_b.
The stack unwinds: function_b resumes and finishes, then function_a resumes and finishes, and finally main resumes and finishes.
Start order: main, function_a, function_b, function_c. Return order: function_c, function_b, function_a, main.
Key Takeaways
- A nested function call pauses the caller while the called function runs.
- Each function call adds a frame to the call stack, and each return removes the top frame.
- Python resumes the calling function at the exact point where the nested call occurred.
- Returned values travel back to the caller, allowing data to pass between functions.
- Local variables belong to their active function frame and are destroyed when that frame is removed.
Key Takeaways
- Nested calls create a waiting chain of functions.
- The call stack records active functions in last-in-first-out order.
- The deepest function returns first, and each caller resumes at its saved call location.
- A returned value passes data back to the caller.
- Local variables exist only while their function is active on the call stack.