Understanding Mutable and Immutable Types
Lists are passed to functions by reference, meaning the function receives access 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…
The List That Changes Elsewhere
A function call can change a list that appears to belong to the caller. This can feel surprising if you expect the function to receive a separate copy. When a list is passed to a function, the function receives access to the original list. If the function modifies that list, the caller sees the modification after the function returns.
What do you think happens?
Before tracing the call, predict what happens to letters after delete_head receives it and removes the element at index 0.
Reveal answer
letters becomes ['b', 'c']
The function parameter and letters refer to the same list object. Removing the element through the parameter modifies that shared object.
Two Names, One List
The key idea is aliasing. The original variable in the caller and the function parameter are aliases for the same list object in memory. The parameter is not a new list containing copied elements. It is another name through which the function can access the existing list.
The diagram shows two names connected to one object. Because there is only one list object, an operation that removes or updates an item changes that object for both names. The caller does not need to receive a returned list to observe the change; its variable already refers to the modified object.
Tracing a Removal
The delete_head Example
A caller has letters containing ['a', 'b', 'c']. The function delete_head receives the list as parameter t and deletes the element at index 0. What does letters contain afterward?
- 1Before the call
The caller's variable letters refers to a list containing ['a', 'b', 'c'].
- 2During the call
The parameter t refers to the same list object. The deletion at index 0 removes 'a' from that object.
- 3After the call
The function returns, but the list object remains modified. The caller's variable letters still refers to that same object.
letters contains ['b', 'c'].
The important event is not that the parameter t disappears when the function ends. The important event is that the list object was changed while t referred to it. Since letters also referred to that object, the caller observes the deletion.
Mutation Versus Reassignment
Changing the list object and changing what the parameter refers to are different operations. A modification such as deleting an element changes the shared list object, so the caller sees it. Reassigning the parameter to a different list does not change the caller's original list. After reassignment, the parameter refers to the new list instead, while the caller's variable continues to refer to the original list.
| Function action | What happens to the list object | What the caller observes |
|---|---|---|
| Modify the list through the parameter | The shared list object changes | The caller sees the modification |
| Reassign the parameter | The parameter refers to a different list | The caller's original list is not affected |
The visible result depends on whether the function changes the shared object or changes only the parameter's reference.
Mutable and Immutable Values
The source contrasts lists with numbers. A number is described as a value; when it is passed to a function and modified inside the function, the original number outside the function stays unchanged. A list is an object, and passing it gives the function a reference to that object. Therefore, changes made to the list through the parameter are visible to the caller.
| Passed item | Description from the source | Effect of a function's modification |
|---|---|---|
| Number | A value | The original number outside the function stays unchanged |
| List | An object accessed through a reference | Changes to the list are visible to the caller |
The distinction explains why modifying a list through a parameter can affect the caller while modifying a passed number does not.
Debugging Unexpected Changes
Unexpected list changes are often caused by a function call that received the list earlier. To investigate, trace every function call that passed the list as a parameter. Then check whether each function removed elements, updated items, or otherwise modified the list object. The function remove_negatives illustrates this pattern: it receives a reference to data and deletes elements through its parameter, so data is changed after the function returns.
Common mistake Assuming that a function receives a separate copy of a list.
What it looks likeA function receives letters and removes the first element, but the caller expects letters to remain unchanged.
Why it failsThe function parameter and letters are aliases for the same list object.
Do this insteadTrace the list object through the call and check for modifications made through the parameter.
Common mistake Treating reassignment and mutation as the same operation.
What it looks likeA function assigns its parameter to a new list and the caller expects its original list to change.
Why it failsReassigning the parameter changes what the parameter refers to; it does not modify the caller's original list.
Do this insteadDetermine whether the function changed the shared list object or only reassigned its parameter.
Common mistake Looking only at the final caller state.
What it looks likeThe list has unexpected contents, but the function call that removed an element is not examined.
Why it failsThe mutation happened inside a function, while the visible result appeared later in the caller.
Do this insteadFollow the sequence of calls and identify every function that received the list.
Apply the Trace
A list named records is passed to a function. Inside the function, the parameter is used to remove one item from the list. Predict whether records changes after the function returns, then explain which two names are aliases and which object was modified.
Hints
- Ask whether the function changed the list object or merely reassigned its parameter.
- The caller's variable and the parameter refer to the same list object when the function receives the list.
- A removal changes the shared list object.
A function receives data and reassigns its parameter to a new list containing [1, 2, 3]. Predict whether the caller's original data list changes. Explain why this differs from deleting an element through the parameter.
Hints
- Reassignment changes what the parameter refers to.
- The caller's variable continues to refer to the original list.
- Deleting an element modifies the shared object; reassignment does not.
Key Takeaways
- 1Passing a list to a function gives the function access to the original list rather than a copy.
- 2The caller's variable and the function parameter are aliases for the same list object.
- 3Mutating the list inside the function changes what the caller sees.
- 4Reassigning the parameter does not affect the caller's original list.
- 5To debug an unexpected change, trace function calls that received the list and inspect which ones modified it.
At a glance
- A list passed to a function is accessed through a reference to the original list object.
- The original variable and the function parameter are aliases for that same object.
- List modifications made through the parameter are visible to the caller.
- Parameter reassignment is different from list mutation and does not change the caller's original list.
- Tracing function calls is the practical way to find the source of an unexpected list mutation.