Understanding Mutable and Immutable Objects
The important question after b = a is not whether the values look equal. The important question is whether both names refer to one object. The is operator checks whether two variables refer to the same object. If a is b returns True, a and b are aliases.
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 Object, Two Names
A useful way to understand Python assignment is to separate variable names from objects. A reference is the association between a variable name and an object. When you assign b = a, Python does not create a second object as part of that assignment. Instead, both names refer to the same object. The second name, b, is an alias for a.
The important question after b = a is not whether the values look equal. The important question is whether both names refer to one object. The is operator checks whether two variables refer to the same object. If a is b returns True, a and b are aliases.
Mutation Travels Through Aliases
What do you think happens?
Suppose a and b refer to the same list, and the first item is changed through b. What will a show afterward?
Reveal answer
The changed list
Lists are mutable. There is one underlying list object, so changing that object through b makes the change visible through a as well.
Following a List Alias
Let a refer to the list [10, 20]. Then let b = a. Change the first item through b. What do a and b represent afterward?
- 1Create the list
The name a refers to one list object containing 10 and 20.
- 2Create the alias
The assignment b = a makes b refer to the same list object. It does not create an independent list.
- 3Mutate through b
Changing the first item through b changes the one shared list object.
- 4Inspect both names
Both a and b now show the list [99, 20], because both names still refer to that same changed object.
Both aliases refer to one list whose first item is now 99.
This behavior is not a Python error. It follows directly from there being only one underlying list object. A modification made through one alias is a modification of that object, so every other alias observes the updated contents.
Lists and Strings Behave Differently
The practical danger of aliasing depends on whether the object is mutable. Lists are mutable: their contents can be changed. Strings are immutable: the string object cannot be changed in place. Therefore, aliases to a list can expose unexpected changes, while aliases to a string do not create the same problem.
An Alias to a String
Let a and b refer to the same string, banana. Then apply an uppercase operation to b and assign the result back to b. What happens to a?
- 1Start with one string
Both a and b can refer to the string banana.
- 2Apply the operation
The uppercase operation does not modify banana in place. It creates the string BANANA.
- 3Rebind b
Assigning the result back to b makes b refer to BANANA.
- 4Inspect a
The name a still refers to banana. The original string was not changed.
a refers to banana, while b refers to the new string BANANA.
Reassignment Breaks One Connection
Mutation and reassignment are different operations. Mutation changes the contents of an existing mutable object. Reassignment changes which object a variable name refers to. If a and b both refer to one list and b is reassigned to another object, a continues to refer to the original list. Only b's connection has changed.
Finding Aliasing Bugs
An aliasing bug occurs when a program unintentionally gives two names access to the same mutable list. A later operation through one name then changes what the other name sees. The bug is often not in the list operation itself; the problem is that the program created a shared reference when it needed independent lists.
Common mistake Assuming b = a creates a separate list
What it looks likeAfter b = a, changing the list through b also changes what a shows.
Why it failsAssignment makes both names refer to the same list object.
Do this insteadDecide whether shared access is intentional. If not, create an explicit copy.
Common mistake Treating a list mutation like string replacement
What it looks likeA list-content change is visible through every alias, but a string operation creates a new string.
Why it failsLists are mutable, whereas strings are immutable.
Do this insteadClassify the object before predicting the effect of an operation.
Common mistake Assuming reassignment changes every alias
What it looks likeReassigning b does not change the object to which a still refers.
Why it failsReassignment changes one variable's reference; it does not automatically change other variable names.
Do this insteadTrack each variable name and the object it currently refers to.
Common mistake Avoiding all assignment instead of controlling shared references
What it looks likeAssignment itself is not always a problem; unintended sharing of mutable objects is.
Why it failsThe source material advises intentional use of aliases rather than avoiding assignment entirely.
Do this insteadUse aliases when shared mutation is intended and copies when independent lists are required.
Copying Lists Intentionally
When two lists should be independent, create an explicit copy instead of assigning the original list to another name. The source material gives two strategies: slicing, as in new_list = old_list[:], and the list constructor, as in new_list = list(old_list). These approaches create an independent list for the situation described here, so changing one list does not intentionally share the same list object with the other name.
old_list = [1, 2]
new_list = old_listold_list and new_list refer to the same list.
old_list = [1, 2]
new_list = old_list[:]old_list and new_list refer to separate lists with the same starting contents.
Choose deliberately between sharing and copying. Shared references are appropriate when multiple names should access one mutable list. Use slicing or the list constructor when the names should have independent lists and later mutations should not be shared.
A variable a refers to a list. You want b to begin with the same contents, but you want later list changes through b to remain independent from a. Which strategy should you use, and why?
Hints
- Ask whether b = a creates a new list or another reference.
- Use one of the two explicit list-copying strategies described in this section.
- Your explanation should mention the difference between one shared object and two independent list objects.
A Reliable Tracing Method
- 1List the variable names involved.
- 2Record which object each name refers to after every assignment.
- 3Check whether an assignment creates an alias, creates a copy, or rebinds a name.
- 4Classify the object as mutable or immutable.
- 5If the object is mutable, ask whether the operation changes that object in place.
- 6Predict what every name referring to that object will show afterward.
- 7Use the is operator when you need to check whether two names refer to the same object.
Tracing a Mixed Scenario
A list is assigned to a, b is made an alias of a, and c is made from an explicit copy of a. Which names are affected when the shared list is mutated?
- 1Track a and b
The assignment b = a gives both names access to the same list object.
- 2Track c
Creating c with a list-copying strategy gives c an independent list with the same starting contents.
- 3Mutate through b
The mutation changes the list shared by a and b.
- 4Compare the results
a and b show the changed contents. c retains its own list contents because it does not refer to the shared list.
The mutation propagates to aliases of the original list, but not to the independently copied list.
Key Takeaways
- 1A reference connects a variable name with an object.
- 2After b = a, both names refer to the same object; b is an alias for a.
- 3Mutating a shared list through one alias changes what every alias sees.
- 4Strings are immutable, so an operation that appears to change a string creates a new string instead.
- 5Use slicing or the list constructor when independent lists are needed.
At a glance
- Aliasing means that multiple variable names refer to one object.
- Lists are mutable, so a mutation through one alias is visible through all aliases.
- Strings are immutable, so string operations create new strings rather than changing the original string in place.
- Reassignment changes one variable's reference, while mutation changes a mutable object's contents.
- Use slicing or
list()to create independent lists when shared mutation is not intended.