Defining and Calling User-Defined Functions
Expressions and variables can both be passed as arguments to user-defined functions, just as they can be passed to built-in functions.
The Value Before the Function
When you call a function, the information inside the parentheses is an argument. That argument may be a literal value, a variable, or an expression such as a calculation or another function call. User-defined functions accept these forms in the same way built-in functions do. The important question is not only what appears in the call, but what value Python has prepared before the function begins running.
Tracing an Expression Argument
What do you think happens?
What does the user-defined function receive when it is called with 'Spam ' * 4?
Reveal answer
The resulting repeated string: 'Spam Spam Spam Spam '.
Python evaluates the expression once before the function is called. The resulting single string is passed to the function, whose parameter then holds that string.
The sequence has three important stages. First, Python computes the expression. Second, it calls the user-defined function with the resulting value. Third, the function's parameter holds that value while the function body runs. The string repetition happens once during the first stage, even if the function later prints the received string twice.
Spam Spam Spam Spam
Spam Spam Spam Spam What a Parameter Receives
An expression is a combination of values, operators, and function calls that Python can evaluate to produce a result. When an expression is used as an argument, the expression is evaluated first and only its result is passed to the function.
A variable works differently in appearance but not in timing. When a variable is passed, Python looks up the value stored in that variable. The function receives that value, not the variable name. Thus, a parameter can receive a string retrieved from a variable just as it can receive a string produced by an expression.
| Argument form | What Python does before the call | What the function receives |
|---|---|---|
| Variable | Looks up the value stored in the variable | The stored value |
| Expression | Evaluates the expression once | The expression's result |
Both forms provide a value to the function; the preparation step differs.
Two Calls, Two Preparation Steps
Comparing a Variable with an Expression
Determine what print_twice receives in each call: print_twice(michael) and print_twice(math.cos(math.pi)).
- 1Variable call
The variable michael stores the string 'Eric, the half a bee.'. Python retrieves that stored string and passes it to
print_twice. - 2Expression call
The expression
math.cos(math.pi)is evaluated once beforeprint_twiceis called. Its result is -1.0, so -1.0 is passed to the function. - 3Function execution
In each case, the parameter bruce holds the received value. The function uses that value when it runs.
The first call receives 'Eric, the half a bee.'. The second call receives -1.0. The function receives values, not the variable name or the unevaluated expression.
Eric, the half a bee.
Eric, the half a bee.
-1.0
-1.0Although the function prints each received value twice, that repetition is part of the function's behavior. It does not mean that the argument preparation happened twice. The expression math.cos(math.pi) was called once, before the function began.
Debugging the Evaluation Boundary
When a user-defined function behaves unexpectedly, separate argument preparation from function execution. First identify whether the call uses a variable or an expression. For a variable, check the value currently stored in it. For an expression, check whether the expression produces the value you expect. Then check how the function uses the value it receives. This trace shows whether the problem occurred before the call or inside the function.
- 1Identify the argument in the function call.
- 2If it is a variable, determine the value stored in that variable.
- 3If it is an expression, evaluate the expression once.
- 4Record the value that reaches the function parameter.
- 5Trace how the function uses that received value.
Mistakes in Argument Tracing
Common mistake Assuming the function receives the variable name
What it looks likeCalling print_twice(michael) and reasoning that the parameter receives the name michael
Why it failsPython looks up the value stored in michael and passes that value to the function.
Do this insteadTrace michael to its stored string value before reasoning about the function body.
Common mistake Assuming an expression is evaluated inside the function
What it looks likeTreating math.cos(math.pi) as if the function receives the unevaluated expression
Why it failsThe expression is evaluated once before the function is called.
Do this insteadWrite down the expression's result first, then use that result as the function's input.
Common mistake Counting repeated use inside the function as repeated argument evaluation
What it looks likeConcluding that 'Spam ' * 4 runs twice because the function prints its parameter twice
Why it failsThe expression is computed once before the call. The function can then use the resulting value more than once.
Do this insteadKeep the argument-evaluation step separate from the later actions performed by the function.
Practice the Trace
For each call, state the exact value that reaches the function parameter before the function body runs: print_twice('Spam ' * 4), print_twice(michael), and print_twice(math.cos(math.pi)). Then state whether the value came from a variable lookup or expression evaluation.
Hints
- An expression contributes its result, not the expression itself.
- A variable contributes the value stored in it, not the variable name.
- The argument is prepared before the function is called.
Practice Check
Predict the value received by print_twice in the call print_twice(math.cos(math.pi)).
- 1Identify the argument
The argument is the expression
math.cos(math.pi), not a variable. - 2Evaluate once
The expression produces -1.0 before the function is called.
- 3Pass the result
The function receives -1.0 as the value of its parameter.
The function receives -1.0. If the function prints its parameter twice, the output contains -1.0 twice.
Key Takeaways
- 1User-defined functions can receive literals, variables, and expressions as arguments.
- 2Python evaluates an argument exactly once before calling the function.
- 3A variable argument contributes the value stored in the variable, not its name.
- 4An expression argument contributes the result of evaluating the expression, not the expression itself.
- 5Tracing the value from the call to the parameter helps separate argument-preparation problems from function-body problems.
At a glance
- Expressions and variables can both be passed to user-defined functions.
- Arguments are evaluated exactly once before the function is called.
- A function receives an argument's value, not a variable name or an unevaluated expression.
- Tracing evaluation makes function output easier to predict and unexpected behavior easier to debug.