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
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 |
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)).
Variable call: The variable michael stores the string 'Eric, the half a bee.'. Python retrieves that stored string and passes it to print_twice.
Expression call: The expression math.cos(math.pi) is evaluated once before print_twice is called. Its result is -1.0, so -1.0 is passed to the function.
Function 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.
- Identify the argument in the function call.
- If it is a variable, determine the value stored in that variable.
- If it is an expression, evaluate the expression once.
- Record the value that reaches the function parameter.
- Trace how the function uses that received value.
Mistakes in Argument Tracing
Assuming the function receives the variable name
Python looks up the value stored in michael and passes that value to the function.
Fix:
Trace michael to its stored string value before reasoning about the function body.Assuming an expression is evaluated inside the function
The expression is evaluated once before the function is called.
Fix:
Write down the expression's result first, then use that result as the function's input.Counting repeated use inside the function as repeated argument evaluation
The expression is computed once before the call. The function can then use the resulting value more than once.
Fix:
Keep 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)).
Identify the argument: The argument is the expression math.cos(math.pi), not a variable.
Evaluate once: The expression produces -1.0 before the function is called.
Pass 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
- User-defined functions can receive literals, variables, and expressions as arguments.
- Python evaluates an argument exactly once before calling the function.
- A variable argument contributes the value stored in the variable, not its name.
- An expression argument contributes the result of evaluating the expression, not the expression itself.
- Tracing the value from the call to the parameter helps separate argument-preparation problems from function-body problems.
Key Takeaways
- 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.