Working with List Methods and Mutations
A reference is the association between a variable name and an object. When you assign b = a, both variables refer to the same object—b is an alias for a.
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
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")
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 |
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
Assuming b = a copies a list.
backup and scores refer to one list, so appending through backup also changes scores.
Fix:
Use backup = scores[:] or backup = list(scores) when backup must be independent.Treating an unexpected shared change as a Python error.
The behavior is the consequence of two names referring to one mutable object.
Fix:
Check whether left is right and decide deliberately whether the shared reference or an independent copy is wanted.Expecting string reassignment and list mutation to behave identically.
The string is immutable. The operation creates a new string and rebinds b instead of changing the original object.
Fix:
Separate 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.
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.
Create the list: source refers to one list containing "a" and "b".
Create the alias: alias = source makes alias another name for that same list.
Create the copy: copy = source[:] creates an independent list with the same current contents.
Mutate 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
- A reference connects a variable name with an object.
- For a list, b = a creates an alias rather than an independent copy.
- A mutation through one list alias is visible through every other alias of that list.
- The is operator checks whether two names refer to the same object.
- Use old_list[:] or list(old_list) when you need an independent list.
- Strings are immutable, so an operation such as b = b.upper() creates a new string and rebinds b instead of changing the original string.
Key Takeaways
- 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.