Debugging Program Flow
Flow of execution is the order in which statements are executed, always proceeding from top to bottom.
Follow the Execution Path
When debugging, ask one question repeatedly: what statement runs next? The flow of execution is the order in which statements are executed. In the ordinary top-to-bottom flow, execution begins with the first statement and proceeds toward the statements below it. Function definitions create an important pause in what you should expect: Python reads and stores the function, but does not immediately run the statements inside it.
Definitions Pause at the Body
Suppose execution reaches a function definition. The program reads the definition and stores the function in memory. It does not execute the statements inside the function at that moment. The flow then continues with the next statement after the definition. This is why a print statement inside a function does not produce output merely because the function has been defined.
Do not mark the statements inside a function as executed merely because the definition appeared earlier. Mark them as executed only when a call reaches that function.
Trace a Call and Return
Tracing greet and farewell
A program defines greet and farewell, then performs these statements in order: print("Program starts"), call greet("Alice"), print("Between calls"), call farewell("Alice"), and print("Program ends"). Trace the execution path.
Read greet: Execution begins at the greet function definition. Python stores the function, but the print statements inside greet do not run.
Read farewell: Execution reaches the farewell function definition and stores it as well. Its print statement also does not run yet.
Start the program: The statement that prints Program starts now executes.
Call greet: Execution jumps into greet. Its two statements run in order, producing Hello, Alice and Welcome!. After the function body finishes, execution returns to the statement after the call.
Continue between calls: The statement that prints Between calls executes next.
Call farewell: Execution jumps into farewell. Its single print statement runs, then execution returns to the statement after the call.
End the program: The statement that prints Program ends executes last.
The output order is Program starts, Hello, Alice, Welcome!, Between calls, the farewell message, and Program ends. The function bodies appear in the output only when their calls are reached.
The important pattern is jump, run, and return. A call does not mean that execution simply continues with the next statement. The call sends execution into the function body. Every statement in that body runs in order. When the body finishes, execution resumes at the exact statement immediately after the call.
Predict Multiple Calls
What do you think happens?
A program defines two functions, then calls the first function, performs a statement between the calls, and calls the second function. Which event happens first: the statements inside the first function, the statement between the calls, or the statements inside the second function?
Reveal answer
Answer: The first function body
When the first call is reached, execution jumps into that function and runs its body before returning to the statement after the call. The statement between the calls runs next, and the second function body runs only when the second call is reached.
For a program with several functions, first mark the definitions as stored rather than executed. Then read the statements below those definitions from top to bottom. Whenever you reach a call, temporarily follow the called function's body from its first statement to its last statement. After that body finishes, return to the next statement after the call and continue the trace.
Mistakes in Flow Tracing
Treating a function definition as an immediate function execution
A definition stores the function but does not run its body.
Fix:
Mark the body as executed only when a call to greet is reached.Continuing on the next line while ignoring a function call
A function call causes execution to jump into the function body first.
Fix:
Trace every statement in the function body, then return to the next line after the call.Putting a function's output in definition order rather than call order
Function bodies run when their calls are reached, and calls determine the execution order.
Fix:
Ignore the bodies during definition and place each body into the trace at its call.
When debugging, write a short execution trace. Record definitions as stored, record ordinary statements when they are reached, and indent or otherwise separate the statements followed inside each function call. This makes the jump into a function and the return to the caller visible.
Practice the Trace
Create an execution trace for a program that defines two functions, reaches a statement before its first call, calls the first function, reaches a statement between the calls, calls the second function, and then reaches a final statement. Label each function definition as stored rather than executed. Then list the function body statements at the point where each call occurs.
Hints
- Begin with the first statement and move downward.
- Do not execute either function body at its definition.
- At each call, trace the called function body before continuing after the call.
- Locate the first statement in the program.
- Move downward, marking each function definition as stored rather than executed.
- When an ordinary statement is reached, place it in the execution order.
- When a function call is reached, jump to that function's first statement.
- Trace the function body from top to bottom.
- Return to the statement immediately after the call and continue.
Keep the Jump-and-Return Model
- Flow of execution is the order in which statements run, normally proceeding from top to bottom.
- A function definition stores the function and does not execute its internal statements.
- A function call jumps into the function body and runs its statements in order.
- After the function body finishes, execution returns to the statement immediately after the call.
- The order of function calls determines where each function body appears in the overall execution trace.
Key Takeaways
- Execution follows statements from top to bottom unless a function call temporarily moves control into a function body.
- Function definitions are stored; their internal statements do not run during definition.
- Function calls run the function body before execution returns to the next statement after the call.
- To debug program flow, trace definitions, ordinary statements, calls, function bodies, and returns in that order.