Understanding Mutable and Immutable Objects
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.
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
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?
Create the list: The name a refers to one list object containing 10 and 20.
Create the alias: The assignment b = a makes b refer to the same list object. It does not create an independent list.
Mutate through b: Changing the first item through b changes the one shared list object.
Inspect 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?
Start with one string: Both a and b can refer to the string banana.
Apply the operation: The uppercase operation does not modify banana in place. It creates the string BANANA.
Rebind b: Assigning the result back to b makes b refer to BANANA.
Inspect 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.
Assuming b = a creates a separate list
Assignment makes both names refer to the same list object.
Fix:
Decide whether shared access is intentional. If not, create an explicit copy.Treating a list mutation like string replacement
Lists are mutable, whereas strings are immutable.
Fix:
Classify the object before predicting the effect of an operation.Assuming reassignment changes every alias
Reassignment changes one variable's reference; it does not automatically change other variable names.
Fix:
Track each variable name and the object it currently refers to.Avoiding all assignment instead of controlling shared references
The source material advises intentional use of aliases rather than avoiding assignment entirely.
Fix:
Use 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.
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
- List the variable names involved.
- Record which object each name refers to after every assignment.
- Check whether an assignment creates an alias, creates a copy, or rebinds a name.
- Classify the object as mutable or immutable.
- If the object is mutable, ask whether the operation changes that object in place.
- Predict what every name referring to that object will show afterward.
- Use 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?
Track a and b: The assignment b = a gives both names access to the same list object.
Track c: Creating c with a list-copying strategy gives c an independent list with the same starting contents.
Mutate through b: The mutation changes the list shared by a and b.
Compare 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
- A reference connects a variable name with an object.
- After b = a, both names refer to the same object; b is an alias for a.
- Mutating a shared list through one alias changes what every alias sees.
- Strings are immutable, so an operation that appears to change a string creates a new string instead.
- Use slicing or the list constructor when independent lists are needed.
Key Takeaways
- 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.