1.3.3   Example: Calling a User-Defined Function

Let us again consider our two simple function definitions and illustrate the process that evaluates a call expression for a user-defined function.

1from operator import add, mul
2def square(x):
3    return mul(x, x)
4
5def sum_squares(x, y):
6    return add(square(x), square(y))
7
8result = sum_squares(5, 12)
Step 4 of 10
line that has just executed

next line to execute

Global
mul
 
add
 
square
 
sum_squares
 
func mul(...)
func add(...)
func square(x)
func sum_squares(x, y)

Python first evaluates the name sum_squares, which is bound to a user-defined function in the global frame. The primitive numeric expressions 5 and 12 evaluate to the numbers they represent.

Next, Python applies sum_squares, which introduces a local frame that binds x to 5 and y to 12.

1from operator import add, mul
2def square(x):
3    return mul(x, x)
4
5def sum_squares(x, y):
6    return add(square(x), square(y))
7
8result = sum_squares(5, 12)
Step 5 of 10
line that has just executed

next line to execute

Global
mul
 
add
 
square
 
sum_squares
 
sum_squares
x5
y12
func mul(...)
func add(...)
func square(x)
func sum_squares(x, y)

The body of sum_squares contains this call expression:

  add     (  square(x)  ,  square(y)  )
________     _________     _________
operator     operand 0     operand 1

All three subexpressions are evaluated in the current environment, which begins with the frame labeled sum_squares(). The operator subexpression add is a name found in the global frame, bound to the built-in function for addition. The two operand subexpressions must be evaluated in turn, before addition is applied. Both operands are evaluated in the current environment beginning with the frame labeled sum_squares.

In operand 0, square names a user-defined function in the global frame, while x names the number 5 in the local frame. Python applies square to 5 by introducing yet another local frame that binds x to 5.

1from operator import add, mul
2def square(x):
3    return mul(x, x)
4
5def sum_squares(x, y):
6    return add(square(x), square(y))
7
8result = sum_squares(5, 12)
Step 6 of 10
line that has just executed

next line to execute

Global
mul
 
add
 
square
 
sum_squares
 
sum_squares
x5
y12
square
x5
func mul(...)
func add(...)
func square(x)
func sum_squares(x, y)

Using this environment, the expression mul(x, x) evaluates to 25.

Our evaluation procedure now turns to operand 1, for which y names the number 12. Python evaluates the body of square again, this time introducing yet another local frame that binds x to 12. Hence, operand 1 evaluates to 144.

1from operator import add, mul
2def square(x):
3    return mul(x, x)
4
5def sum_squares(x, y):
6    return add(square(x), square(y))
7
8result = sum_squares(5, 12)
Step 9 of 10
line that has just executed

next line to execute

Global
mul
 
add
 
square
 
sum_squares
 
sum_squares
x5
y12
square
x5
Return
value
25
square
x12
Return
value
144
func mul(...)
func add(...)
func square(x)
func sum_squares(x, y)

Finally, applying addition to the arguments 25 and 144 yields a final return value for sum_squares: 169.

1from operator import add, mul
2def square(x):
3    return mul(x, x)
4
5def sum_squares(x, y):
6    return add(square(x), square(y))
7
8result = sum_squares(5, 12)
End
line that has just executed

next line to execute

Global
mul
 
add
 
square
 
sum_squares
 
result169
sum_squares
x5
y12
Return
value
169
square
x5
Return
value
25
square
x12
Return
value
144
func mul(...)
func add(...)
func square(x)
func sum_squares(x, y)

This example illustrates many of the fundamental ideas we have developed so far. Names are bound to values, which are distributed across many independent local frames, along with a single global frame that contains shared names. A new local frame is introduced every time a function is called, even if the same function is called twice.

All of this machinery exists to ensure that names resolve to the correct values at the correct times during program execution. This example illustrates why our model requires the complexity that we have introduced. All three local frames contain a binding for the name x, but that name is bound to different values in different frames. Local frames keep these names separate.