🔁 Mutable vs Immutable in Python – A Deep Dive (With Mental Models)

Understanding the difference between mutable and immutable objects in Python is foundational for writing efficient, bug-free code. This concept affects how variables behave, how functions handle arguments, and how memory is managed behind the scenes.
Let’s break it down clearly, with examples and memory models — no memorization needed.
✅ What Are Mutable and Immutable Objects?
In simple terms:
Mutable objects: Can be changed after they are created.
Immutable objects: Cannot be changed after creation.
Immutable Types | Mutable Types |
int , float , bool | list , dict , set |
str , tuple , bytes | bytearray , array.array |
frozenset | Custom mutable classes |
🧪 Example: Strings (Immutable)
username = "Manas"
print(username) # Manas
username = "Bits of Python by Manas"
print(username) #
Here’s what actually happens:
username = "Hitesh"
→ A string object is created in memory andusername
points to it.username = "Chai aur Code"
→ A new string object is created, andusername
is now bound to that. The old string"Hitesh"
is no longer referenced.
You didn’t modify the original string; you reassigned the variable.
🧪 Example: Integers (Immutable)
x = 10
y = x
x = 15
print(x) # 15
print(y) # 10
Even though y = x
, modifying x
later doesn't affect y
.
Why? Because:
Integers are immutable.
When
x = 15
, a new object is created in memory andx
now points to it.y
still points to the original10
.
🧪 Example: Lists (Mutable)
a = [1, 2, 3]
b = a
a.append(4)
print(a) # [1, 2, 3, 4]
print(b) # [1, 2, 3, 4]
Both a
and b
reference the same object. Since lists are mutable, modifying the object through one reference reflects in the other.
🧠 Behind the Scenes: Memory and Object Identity
In Python, everything is an object. Every object has:
Identity: Its memory address (check with
id(obj)
)Type: Like
int
,str
,list
, etc.Value: The actual data it holds
When you assign a variable, you're creating a reference to an object — not copying the object itself.
⚠️ Gotcha: Mutable Default Arguments
def foo(bar=[]):
bar.append(1)
return bar
print(foo()) # [1]
print(foo()) # [1, 1]
The default list is shared across function calls! Always use None
as the default:
def foo(bar=None):
if bar is None:
bar = []
bar.append(1)
return bar
🔁 Pass-by-Assignment in Python
Python’s function arguments are passed by assignment (also called "pass-by-object-reference"):
You pass a reference to the object, not the actual value.
Whether the function can modify the object depends on its mutability.
def modify_list(lst):
lst.append(10)
my_list = [1, 2]
modify_list(my_list)
print(my_list) # [1, 2, 10]
But with immutable types:
def change_number(n):
n = n + 1
x = 5
change_number(x)
print(x) # 5 (unchanged)
🗑️ Garbage Collection
When no variable refers to an object anymore, Python will automatically clean it up:
name = "Manas"
name = "Bits of Python by Manas"
# "Manas" is now unreferenced → garbage collected
🧰 Tools and Tips
Use
id(obj)
to check if two variables refer to the same object.Use
copy()
ordeepcopy()
when working with mutable types and you need independent copies.Convert lists to tuples when immutability is preferred.
Be careful when passing mutable objects to functions.
🧠 Key Takeaway
It’s not just about whether you can "change a value." It’s about whether the object in memory can be altered without creating a new one.
If yes → it’s mutable.
If no → it’s immutable.
💬 Over to You
Which part of mutable vs immutable tripped you up when learning Python? Ever hit a bug because of it? Share your thoughts in the comments!
Thanks You!
Subscribe to my newsletter
Read articles from Manas Shinde directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
