Working with List Methods and Mutations
For mutable objects such as lists, that intuition is wrong.
The Copy Assumption
A common first intuition is that assigning b = a creates a separate copy of whatever a refers to. For mutable objects such as lists, that intuition is wrong. The assignment makes b another name for the same object. This relationship is called aliasing.
What do you think happens?
After this code runs, what will a contain when the final line is evaluated?
values = [4, 8]
alias = values
alias.append(12)Reveal answer
[4, 8, 12]
The assignment alias = values creates an alias. Both names refer to one list, so append changes the shared list. The change is visible through values as well as alias.
Following a Shared List
A reference is the association between a variable name and an object. When you assign b = a, both variables refer to the same object. The second variable is an alias for the first variable's object.
items = ["pen", "book"]
other_items = items
other_items.append("lamp")- 1The first line creates one list object and associates the name items with it.
- 2The second line does not create another list. It associates
other_itemswith the existing list. - 3The third line mutates the shared list by adding "lamp".
- 4Because items and
other_itemsrefer to the same object, reading either name shows the changed list.
- items is bound to a list containing "pen" and "book".
other_items= items gives the second name a reference to that same list.- append changes the list object through
other_items.
items: ["pen", "book", "lamp"]
other_items: ["pen", "book", "lamp"]The important point is not the particular method name. Whether a list is changed through item assignment, append, or remove, the change is made to the one underlying list object. Every alias exposes the updated contents.
Checking Identity and Separation
The is operator checks whether two variables refer to the same object in memory. If a is b evaluates to True, a and b are aliases: they are two names for one list. If you need independent lists, the names must refer to different list objects rather than sharing one.
True
False
FalseHere, original and alias share one list, so the first identity check is True. Slicing with original[:] and constructing list(original) create explicit list copies. Those names refer to independent lists, so the later identity checks are False.
Lists and Strings Compared
| Object type | Can the object change in place? | Effect of assigning a second name | Effect of an apparent change |
|---|---|---|---|
| List | Yes | The second name can be an alias for the same list | A mutation is visible through every alias |
| String | No | Aliasing is not a practical problem | An operation that appears to change it creates a new string |
The source distinguishes mutable lists from immutable strings.
a: "banana"
b: "BANANA"The assignment b = a initially gives both names the same string object. However, b = b.upper() does not modify the string "banana" in place. It creates the new string "BANANA" and rebinds b to that new string. The name a still refers to "banana". This is why the same aliasing pattern is harmless here: strings cannot be mutated in place.
Finding and Preventing Aliasing Bugs
Common mistake Assuming b = a copies a list.
What it looks likescores = [10, 20] backup = scores backup.append(30)Why it failsbackup and scores refer to one list, so appending through backup also changes scores.
Do this insteadUse backup = scores[:] or backup = list(scores) when backup must be independent.
Common mistake Treating an unexpected shared change as a Python error.
What it looks likeleft = ["red"] right = left right[0] = "blue"Why it failsThe behavior is the consequence of two names referring to one mutable object.
Do this insteadCheck whether left is right and decide deliberately whether the shared reference or an independent copy is wanted.
Common mistake Expecting string reassignment and list mutation to behave identically.
What it looks likea = "banana" b = a b = b.upper()Why it failsThe string is immutable. The operation creates a new string and rebinds b instead of changing the original object.
Do this insteadSeparate mutation from rebinding: list changes can affect aliases, while string operations produce new strings.
Be intentional when assigning one variable to another mutable object. Use a direct assignment when shared changes are wanted. Use slicing or the list constructor when each variable should have an independent list. The goal is not to avoid assignment; it is to understand whether the assignment creates an alias or an explicit copy.
records = ["A", "B"]
working = records
working.append("C")Both names refer to the same list, so records also contains "C".
records = ["A", "B"]
working = records[:]
working.append("C")working is an independent copy, so records remains ["A", "B"] while working contains ["A", "B", "C"].
Practice the Reference Trace
Trace the names and list contents after each line. Then decide whether the final change is visible through both names.
first = ["north", "south"]
second = first
second.remove("north")What are first and second after the remove operation? Would your answer change if the second line were replaced with second = list(first)?
Hints
- Ask whether second = first creates another list or another name for the existing list.
- The remove operation changes a list object.
- The list constructor is one of the explicit copying strategies.
Tracing an Alias and a Copy
Start with source = ["a", "b"]. Create alias = source and copy = source[:]. Mutate alias by adding "c". Determine the contents reached through all three names.
- 1Create the list
source refers to one list containing "a" and "b".
- 2Create the alias
alias = source makes alias another name for that same list.
- 3Create the copy
copy = source[:] creates an independent list with the same current contents.
- 4Mutate the alias
Adding "c" through alias changes the shared list. source sees that change, but copy does not.
source is ["a", "b", "c"], alias is ["a", "b", "c"], and copy is ["a", "b"].
Key Takeaways
- 1A reference connects a variable name with an object.
- 2For a list, b = a creates an alias rather than an independent copy.
- 3A mutation through one list alias is visible through every other alias of that list.
- 4The is operator checks whether two names refer to the same object.
- 5Use
old_list[:] orlist(old_list)when you need an independent list. - 6Strings are immutable, so an operation such as b =
b.upper()creates a new string and rebinds b instead of changing the original string.
At a glance
- Aliasing means that multiple variable names refer to one object.
- Because lists are mutable, changing a list through one alias changes what every alias sees.
- The is operator can reveal whether two names share the same object.
- Slicing and the list constructor create independent list copies.
- Strings cannot be changed in place, so apparent changes create new string objects instead.