main
No variables
The previous chapter used parameters with numbers and strings. A function can also receive a list. To understand when a call changes that list, we need to look more closely at how names refer to values.
In Part 1, a variable gave a value a name. We can describe that relationship more precisely:
A variable is a name bound to an object.
An object is a Python value. Integers, strings, booleans, and lists are all objects. Numbers, strings, and booleans are immutable: they cannot be changed in place. Lists are mutable: their contents can change.
Assignment makes a name refer to an object:
score = 10This can be pictured as:
score ──► 10Assignment and function calls do not automatically duplicate the object.
Run this as a separate program:
def double(number): result = number * 2 return result
answer = double(5)print(answer)print(result)Inside the function, number and result are local names. The call returns 10, which the main program stores as answer. The first print displays 10, so the returned value is available to the caller.
The next statement, print(result), stops the program with a NameError. Python reports this error when it cannot find a name used by the program. Here, result is available inside double, but the main program has no name result.
The part of a program where a name can be used is its scope. For now, the most important distinction is:
Parameters are local names too. Each call has its own local names, even when several calls use the same function definition.
message = "outside"
def show_message(): message = "inside" print(message)
show_message()print(message)This prints:
insideoutsideThe two names are spelled alike but belong to different scopes. Assigning the local message does not reassign the outside message.
A function can read a name from outside when it has no local name with the same spelling:
tax_rate = 0.24
def tax(amount): return amount * tax_rateThis works, but it gives tax a dependency that is not visible in its parameters. A clearer small calculation function is often:
def tax(amount, tax_rate): return amount * tax_rateThis is a rule of thumb rather than an absolute prohibition: prefer passing required information as a parameter when doing so makes the function’s inputs clearer.
When a function is called, the argument expression is evaluated and the parameter becomes a local name for that object:
At the beginning of the call, the names can be pictured as:
score ──► 10number ──► 10The integer object 10 is immutable: it cannot be changed in place. The expression number + 1 produces the integer 11, and assignment rebinds only local number:
score ──► 10number ──► 11This does not assign to the caller’s name score. The same reassignment rule applies to every parameter, including list parameters.
Trace what happens to names inside and outside a function during a call:
Lists can be supplied as arguments just like numbers and strings:
def total_values(values): total = 0
for value in values: total = total + value
return totaltotal_values([5, 8, 2]) returns 15. The accumulator and loop are familiar from Part 2; only the function boundary is new.
A list is mutable, so a function can also modify the list object supplied by its caller:
Step through the call. The caller’s results and local scores refer to one list. append changes that list while both names keep referring to it.
Code
Step 1 of 6
State
No variables
Objects
No objects yet
Output
No output yet
When the call finishes, its local frame closes. The caller observes the changed list through results.
Compare the preceding example with:
Watch the two names again. The assignment creates a separate list and makes only local scores refer to it. The caller’s results stays bound to the original list.
Code
Step 1 of 6
State
No variables
Objects
No objects yet
Output
No output yet
The program prints [10, 12]. Rebinding scores did not modify the original list or rebind results.
scores.append(15) # modifies the existing listscores = [0, 0, 0] # rebinds the local nameThe rule did not change between the number and list examples:
This behavior is not specific to parameters:
first = [1, 2]second = firstsecond.append(3)
print(first)print(second)Both lines print [1, 2, 3]. Assignment did not create an independent list. It bound second to the same list as first.
More than one name for the same object is called aliasing. When aliases refer to a mutable list, a change made through one name is visible through the others.
Trace reassignment, mutation, and aliasing side by side:
A function whose stated purpose is to change a supplied list may reasonably use indexed mutation:
def increase_each(values): for index in range(len(values)): values[index] = values[index] + 1Apply that pattern to a conditional cleanup:
0 / 15 points
Define a function named replace_negative_readings with one parameter, values.
Use an indexed loop to replace every negative item in the existing list with 0. Leave zero and positive items unchanged. The function must modify the list supplied by its caller, print nothing, and return None; do not return either the original list or a new list.
For example:
readings = [4, -2, 0, -7, 5]replace_negative_readings(readings)print(readings)prints:
[4, 0, 0, 0, 5]An empty list remains empty. Submit only the function definition.
A calculation function should usually return its result without unexpectedly changing its input. The earlier total_values function does this: it reads the items and returns their total without changing the list.
Sometimes a caller needs an updated list while keeping the original values. The function can return a separate list instead of changing the supplied one. Building a result list uses the same loop and list operations as in Part 2.
Practise that choice by recording another sensor reading:
0 / 15 points
Define with_reading_added(readings, reading) for a sensor log. The parameter readings is a list of numbers, and reading is one more number to record. The numbers may be negative, zero, positive, or decimal values.
Return a new list containing all the original readings in their original order, followed by the additional reading. Keep repeated values. An empty log produces a list containing just the additional reading.
Leave readings unchanged. The returned list must be separate from the original: changing either list afterward must not change the other. Each call must create its own result, so recording another reading later must not change a list returned by an earlier call.
For example:
original = [4, -2, 0, 4]extended = with_reading_added(original, 1.5)print(original)print(extended)prints:
[4, -2, 0, 4][4, -2, 0, 4, 1.5]The function must not read input or print anything. The loops and list operations from Part 2 are enough for this task. Try calls with an empty log and repeated readings as well as the example above. Remove temporary calls before submitting, leaving your function definitions.
Returning a list does not automatically copy it. In the exercise, the function must create a separate list because the caller needs both the original log and the extended log. The earlier cleanup exercise instead changes the existing list and returns None. Follow the function’s stated purpose when deciding which behavior to use.
Finish by explaining why mutation and reassignment have different effects:
0 / 10 points
Explain in plain language why this program prints [1, 5]:
def revise(values): values.append(5) values = [0]
numbers = [1]revise(numbers)print(numbers)In three or four sentences, describe what numbers and the parameter values refer to at the start of the call. Distinguish the effect of .append(5) from the later assignment values = [0].
AI Study Aid
We're looking into to what extent AI-generated diagrams could help with studying.
Use this study aid to generate an AI-generated visual summary of the material you just studied.
Each diagram style emphasizes the content in a different way, so you can choose the focus
that feels most useful.
Using the diagram generator is voluntary and does not affect your course progress. We encourage
you to try it out and see if it helps, but it's totally up to you! Your answers help us understand
how to make better study aids in the future.