Flow of Execution
Flow of execution is simply the order in which lines run. Without functions it is top to bottom, and you can read it off the page. A call breaks that: it jumps into the function, runs it, and jumps back to exactly where it left. Learning to trace that jump is the whole of this lesson.
1A call jumps, and comes back
def double(n):
print(' inside double, n =', n)
return n * 2
print('1. program starts')
answer = double(5)
print('2. back in the main program, answer =', answer)
print('3. program ends')1. program starts inside double, n = 5 2. back in the main program, answer = 10 3. program ends
- 1
def double(n):
Stores the function. Runs none of its body. - 2
print('1. program starts')
Prints the first line. - 3
answer = double(5)
Reaches the call — and jumps into the function, taking 5 with it. - 4
print(' inside double…')
Inside the function now. - 5
return n * 2
Works out 10, ends the function, and jumps back to the call. - 6
answer = 10
The call has become 10, so the assignment finishes. - 7
print('2. back in…')
Carries on from where it left off. - 8
print('3. program ends')
And on to the end.
return, or simply ran out of lines — it resumes at that exact spot, with the returned value in hand.2def does not run the body
Worth seeing on its own, because it decides the answer to almost every “predict the output” question about functions:
print('A')
def f():
print('B')
print('C')
f()
print('D')A C B D
A, C, B, D. The B is printed third, not second — because reaching the def only stored the function, and the body waited until the call on the second-to-last line.
3A function that calls another function
def inner():
print(' inner runs')
def outer():
print(' outer starts')
inner()
print(' outer ends')
print('main starts')
outer()
print('main ends')main starts
outer starts
inner runs
outer ends
main endsThe indentation of the output is a picture of the flow. Each call goes one level deeper, and each finish comes back one level — and the two main lines sit outside everything because that is where the program started and ended.
inner() finishes it returns to the middle of outer(), and when outer() finishes it returns to the middle of the main program. Each pending call is remembered until the one inside it is done — the same last-in-first-out idea as the stack you will meet later in this course.4Definitions can be in any order — calls cannot
Since def only stores, one function may mention another that is defined further down. By the time anything is called, every definition in the file has been read:
def a():
b() # b does not exist yet — and that is fine
def b():
print('b ran')
a() # by now, both existb ran
Move the a() call to the top of the file, though, and it fails — because at that moment the def lines have not run yet. The rule is about when the call happens, not when the mention is written.
5How to trace one by hand
- 1. Skip every
defblock on the way down. They store, they do not run. - 2. Start at the first line that is not inside a function, and follow it down.
- 3. At a call, put your finger on that line and jump to the function. Write the arguments beside its parameters.
- 4. At
return— or the end of the body — go back to your finger, with the value. - 5. Write down each
printas you pass it. That list, in order, is the answer.
Work that one out on paper before you run it. The answer is start, then in thrice, then in twice, then 12, then end — and the 12 appears after both function messages because the outer print cannot print until the call inside it has produced a value.
6Recap
A, C, B, D — the body waits until a call reaches it, however early the def appears.
Python jumps in, runs the body, and resumes at exactly the line it left, with the returned value.
Each one goes a level deeper and comes back a level. Python remembers every pending call.
Skip the defs, follow the main program, jump at each call, come back at each return, and write down the prints.
- 1
Predict the output of the A / C / B / D program before running it.
Hint · Skip the def on the way down, then come back for the call.
- 2
Write three functions where the first calls the second and the second calls the third, with a print in each.
Hint · Indent each message a little further and the output draws the nesting for you.
- 3
Put a
printafter areturnand check whether it ever appears.Hint · It cannot.
returnends the function before that line is reached. - 4
Trace
thrice(4)on paper, then run it and compare.Hint · The trick is that
print(thrice(4))has to wait for the call before it can print anything at all.
What does the A / C / B / D program print, in order?
After a function finishes, where does the program carry on from?
Why can a() mention b() when b is defined below it?