Mutable vs. Immutable Data Types
Passing a list to a function gives the function a reference to the original list, not a copy.
Try it: Names and Objects
How Python variables are names bound to objects: assignment never copies, mutating a list is seen through every name that points at it, and ints are replaced rather than changed.
How it works
- name = object binds a name; b = a makes b point at the same object.
- Mutating a list (append, +=) changes the one shared object.
- a = a + [x] and y = y + 1 create NEW objects and re-point one name.
- Passing a list to a function passes the reference, so the function can change it.
Default run (7 steps): Two names, one list. Every name is a label that points at an object. … Printed: [1, 2, 3] / True
Simplified: Six fixed scripts on a tiny heap; you choose the values. Object numbers are illustrative, not real id() values.
Loading the simulation…
One List, Two Names
A function parameter can refer to the same list that the caller already has. The function does not receive a copy automatically. Therefore, if the function modifies that list in place, the caller sees the modification after the function finishes.
Passing a list to a function gives the function a reference to the original list, not a copy.
Tracing an In-Place Change
What do you think happens?
After this function call, what will the caller's list contain?
Reveal answer
[10, 20, 30]
append modifies the existing list object. The function parameter refers to the caller's original list, so the caller sees the added element.
def add_item(values):
values.append(30)
items = [10, 20]
add_item(items)
print(items)- 1The caller creates a list containing 10 and 20.
- 2The caller passes that list to
add_item. - 3Inside the function,
values.append(30)changes the existing list. - 4After the function returns, printing items shows the changed contents.
- The parameter values refers to the list passed by the caller.
- append modifies that existing list in place.
- The caller's variable items still refers to the modified list.
[10, 20, 30]The important event is not merely that the function receives a parameter. The important event is what the function does with the referenced list. An operation such as append changes the existing object, so the change is visible through the caller's variable as well.
Mutation Versus New Lists
| Operation | Effect on the existing list | Resulting behavior |
|---|---|---|
| append | Modifies the existing list | The caller sees the change when the list was passed to a function |
| del | Modifies the existing list | The caller sees the deletion when the list was passed to a function |
| + | Does not modify the existing list | Creates a new list |
| Slicing | Does not modify the existing list | Creates a new list |
The key distinction is whether an operation changes the existing list object or creates a separate list.
[10, 20, 30]
[10, 20, 30, 30]
[10, 20]In this example, the original items list remains [10, 20, 30]. The plus operation produces a new list, and slicing produces another new list. Because neither operation modifies items in place, the caller's original list is preserved.
Aliasing and Shared Changes
[1, 2, 3]
[1, 2, 3]The same reference behavior can occur without a function. If two variables refer to one list, an in-place operation through either variable changes the shared list. Both variables then show the changed contents.
Two Function Design Choices
[1, 2, 3, 4]
[1, 2, 3]
[1, 2, 3, 4]Use an in-place design when the caller expects the original list to change. Use a return-new-list design when preserving the original matters. Returning a new list is often safer because it avoids surprising side effects and lets the caller decide whether to keep the new result.
Mistakes with List References
Common mistake Assuming that passing a list creates a copy.
What it looks likeA function receives values and uses values.append(5), but the caller expects its original list to remain unchanged.
Why it failsPassing the list gives the function a reference to the original list, not a copy. append modifies that existing list.
Do this insteadUse an operation such as + or slicing to create a new list when the original must be preserved.
Common mistake Treating append and + as equivalent.
What it looks likeComparing values.append(5) with values + [5] as though both leave the original list in the same state.
Why it failsappend modifies the existing list, while + creates a new list.
Do this insteadTrack whether the operation changes the existing object or produces a separate result.
Common mistake Ignoring the function's design intention.
What it looks likeCalling a function that modifies its list parameter when the caller needs to retain the original contents.
Why it failsIn-place modification creates a side effect visible to the caller.
Do this insteadChoose a function that returns a new list, or design the function to create and return one.
Practice the Trace
For each function, predict the contents of the list named numbers after the call. Then decide whether the function modifies the original list or returns a new one.
Function A:
def change(numbers):
numbers.append(8)Function B:
def change(numbers):
return numbers + [8]Use numbers = [2, 4] for both functions.
Hints
- Ask whether the operation is append or +.
- Remember that a function parameter refers to the list passed by the caller.
- For the second function, identify which variable receives the returned list.
Tracing Two Different Designs
Start with numbers = [2, 4]. Compare a function that calls append with a function that returns numbers + [8].
- 1Function A operation
append modifies the existing list in place.
- 2Function A result
After calling
change(numbers), numbers contains [2, 4, 8]. - 3Function B operation
The + operation creates a new list rather than modifying the existing list.
- 4Function B result
The original numbers remains [2, 4]. The new list is [2, 4, 8] and must be captured from the function's return value.
Function A changes the caller's list. Function B preserves the caller's list and provides a new list as its return value.
The Decision to Remember
- 1Passing a list to a function gives the function a reference to the original list, not a copy.
- 2In-place operations such as append and del modify the existing list, so the caller sees the change.
- 3The + operator and slicing create new lists, so the original list remains unchanged.
- 4Modify a list in place when the caller expects the original to change.
- 5Return a new list when preserving the original avoids surprising side effects and gives the caller control.
Key Takeaways
- A function parameter referring to a list refers to the caller's original list.
- In-place operations change that shared list and make the change visible to the caller.
- The + operator and slicing create new lists instead of changing the original.
- Function design should make the choice between mutation and preservation intentional.
- Returning a new list is often safer when the original list should remain available to the caller.