Mutation means changing the internal state of an object in place — without creating a new object.
So when an object is mutable, it can be modified after creation. When it’s immutable, any “change” creates a new object instead.
| Concept | C++ Equivalent | Python Meaning |
|---|---|---|
| Immutable object | const object or value type |
You cannot change its content; any modification makes a new object |
| Mutable object | non-const pointer to heap object | You can change its contents in place (same address / ID) |
a = 10
print(id(a)) # e.g. 139887812806032
a += 1
print(id(a)) # different → new object createdEven though you “changed” a, what really happened:
- Python created a new int object (11).
- Then re-bound the name
ato that new object.
nums = [1, 2, 3]
print(id(nums))
nums.append(4)
print(id(nums)) # same → mutated in placeHere, the same list object’s contents changed; Python didn’t create a new list.
Mutation affects:
- Shared references
- Function defaults
- Thread safety
- Performance
Example — shared reference side effect:
a = [1, 2]
b = a
a.append(3)
print(b) # [1, 2, 3] — changed too!Both a and b refer to the same list in memory.
Mutating one mutates the other because they share the same underlying object.
| Type | Mutable? | Example of Mutation |
|---|---|---|
int |
❌ | x += 1 → new object |
float |
❌ | y *= 2.0 → new object |
str |
❌ | "abc".replace("a", "x") → returns new string |
tuple |
❌ | You cannot modify any element |
| Type | Mutable? | Example of Mutation |
|---|---|---|
list |
✅ | lst.append(5) |
dict |
✅ | d["key"] = "value" |
set |
✅ | s.add(3) |
bytearray |
✅ | b[0] = 65 |
| Custom classes | ✅ | You can freely assign new attributes |
a ───► [ 1, 2, 3 ] (object ID: 1001)
b ───┘ same object
b.append(4)
↓
[ 1, 2, 3, 4 ] (same ID: 1001)
No new object is made — internal state changed.
x ───► 10 (object ID: 2001)
x += 1
x ───► 11 (object ID: 2002)
Different object; the original 10 still exists until garbage-collected.
- Updating shared data structures efficiently.
- Modifying stateful objects (like caches, buffers, queues).
-
Unintended side effects (multiple references).
-
Hidden state changes.
-
Using mutable objects as default arguments:
def f(x, data=[]): # bad data.append(x) return data
- Mutable: can change contents without changing identity (
id()stays same).- Immutable: any “change” makes a new object (
id()changes).
# Immutable
x = "abc"
print(id(x))
x += "d"
print(id(x)) # new string → new ID
# Mutable
lst = [1, 2]
print(id(lst))
lst += [3]
print(id(lst)) # same list → mutated in placeWould you like me to make a short visual diagram (image) showing how Python names, objects, and mutation differ between mutable and immutable types (like pointers vs values in C++)? It’s a great mental model for mastering Python behavior.