Side Effects vs. Return Values
Void functions perform actions but return None, not a usable value.
Try it: Defining and Calling Functions
What def and a call really do: def only creates a function object, a call binds the arguments to the parameters in a new frame and runs the body, return ends the call at once and its value replaces the call expression in the caller — while a function that only prints gives back None.
How it works
- def name(params): creates a function object and binds the name to it; the body does not run yet.
- A call evaluates its arguments, opens a new frame, binds each parameter to its argument and runs the body from the top.
- return value ends the call immediately (lines after it never run) and sends the value back; the frame and its local names are discarded.
- In the caller, the call expression is replaced by the returned value, which can be stored, used in a larger expression or passed to another call.
- A body that ends without return (e.g. one that only prints) makes the call evaluate to None — so x = f() stores None and None + 1 raises TypeError.
- Calling a name before its def has run raises NameError: the function does not exist yet.
Default run (18 steps): The program is ready. Python runs it from the top, one statement at a time. Idea: the value a call returns is what the caller stores. … The program finished. Console: 8.
Simplified: Four fixed, hand-written programs; you edit the literal arguments, switch the body between return and print, and move the def after the calls. The lab's own small evaluator runs them (your input is never executed as code); every variant was run in CPython 3.12 and the lab reproduces its sys.settrace line/call/return/exception events, its output and its error message. Functions are shown as <function name> without a memory address.
Loading the simulation…
The Visible Action Trap
Many learners expect every function call to produce a value that can be stored and reused. That expectation seems reasonable when a call visibly does something. For example, print displays text on the screen. However, the action performed by a function and the value returned to its caller are separate things. A function can perform a visible action while returning no usable value.
What do you think happens?
What do you think result contains after the function call performs its printing action?
Reveal answer
None
The printing action happens on the screen, but the function does not provide a usable return value. Python represents that absence with the special value None.
Two Kinds of Function Results
A void function performs an action, often a side effect such as printing, but returns None rather than a usable value. A fruitful function computes and returns a meaningful value that the caller can store and use in later calculations.
| Function kind | What happens when it runs | What the caller receives |
|---|---|---|
| Void function | It performs an action or side effect | None |
| Fruitful function | It computes a result | A meaningful value |
The action performed by a function is separate from the value it returns.
The distinction is not whether the function call appears to do something. A void function may clearly change what you see on the screen or perform another action. The distinction is whether the caller receives a meaningful value for storage, calculation, or another operation.
def print_twice(text):
print(text)
print(text)
result = print_twice('Bing')
print(result)- 1First,
print_twice('Bing')is called. - 2Next, 'Bing' appears twice as the function performs its action.
- 3Then the result of the call is assigned to result.
- 4Finally,
print(result)displays None.
- The function performs its printing action twice.
- The call produces screen output before assignment finishes.
- The variable receives None because the function does not return a usable value.
- Printing the variable displays None.
Bing
Bing
NoneAssignment Stores None
Assignment does not capture everything a function does. It captures the value produced by the function call. In the print_twice example, the two lines of screen output are the side effect. The value assigned to result is None. The sequence is therefore: call the function, observe its action, assign its returned value, and then observe None when the variable is displayed.
Tracing a Void Function Assignment
Determine what happens when the result of an action-oriented function is assigned to a variable.
- 1Call
The function is called and performs its action by printing the text twice.
- 2Return
The function does not provide a usable value, so Python represents the absence of that value as None.
- 3Assignment
The variable receives the function call's result, so the variable contains None rather than the printed text.
- 4Later use
Displaying the variable shows None. The screen output and the returned value were separate results.
The function performs its action, but the assigned variable contains None.
What None Means
None is a special Python value with its own type, NoneType. It is not merely an empty slot, and it is not the text string 'None'. The two may look similar when displayed, but they represent different things. None signals that a function did not produce a usable value for its caller, while 'None' is text.
| Item | Meaning | Type |
|---|---|---|
| None | A special value representing no usable return value | NoneType |
| 'None' | Text containing the characters N, o, n, e | String |
Similar spelling does not make None and 'None' the same value.
Tracing an Unexpected None
A None-related bug usually begins with an incorrect expectation about the function's result. The caller expects a meaningful value, but the function is void or reaches a path that does not provide one. Trace the call from the caller into the function, identify the action it performs, and then check what value reaches the assignment. If that value is None, the function did not produce the result the caller needs.
Common mistake Assuming visible output is the function's return value
What it looks likeA function prints text, so the caller assumes the assigned variable contains that text.
Why it failsPrinting is an action or side effect. The function can perform that action and still return None.
Do this insteadTrace the value returned by the call separately from what appears on the screen.
Common mistake Using a variable containing None in a calculation
What it looks likeA variable receives the result of a void function and is then treated as a number.
Why it failsNone is not a usable calculation result.
Do this insteadIdentify whether the called function is fruitful before using its result in a calculation.
Common mistake Confusing None with the string 'None'
What it looks likeCode treats None and 'None' as interchangeable because they look similar when displayed.
Why it failsNone has type NoneType, while 'None' is text.
Do this insteadKeep the special value None conceptually separate from the string 'None'.
Practice the Distinction
A function call produces visible output, and its result is assigned to a variable. Explain separately what the function did as an action and what value the variable received. Then classify the function as void or fruitful and explain why.
Hints
- Do not treat screen output and a returned value as the same result.
- Ask whether the caller received a meaningful value that can be used later.
- If the function did not return a usable value, identify the special value Python uses.
You expected a variable to contain a value for a calculation, but debugging shows that it contains None. What should you inspect first: the screen output, the function's action, or the value returned to the caller? Explain your choice.
Hints
- The bug concerns the value assigned to the variable.
- A function can complete an action without returning a usable value.
- Trace the function call and identify whether it is void or fruitful.
Key Takeaways
- 1A void function performs an action but returns None rather than a usable value.
- 2A fruitful function returns a meaningful value that the caller can store and use.
- 3Assigning the result of a void function stores None in the variable.
- 4None is a real Python value of type NoneType, not the string 'None'.
- 5When an expected value becomes None, trace the function call and check whether the function actually returns a usable result.
At a glance
- Side effects describe what a function does; return values describe what the caller receives.
- Void functions return None, even when their actions are visible.
- Fruitful functions return meaningful values that can be stored and used.
- None and the string 'None' are different, and None has type NoneType.
- An unexpected None is a debugging clue that the caller did not receive the value it expected.