Python : Understanding Mutable and Immutable Objects
I’ll keep it short, sharp, and simple: in Python, everything is an object. What does that mean? It means every value, whether it’s an integer, string, list, function, or even a class, is an object stored somewhere in memory.
Every object has an identity (id), a type (type), and a value, which is why you can inspect them using built-in functions like id() and type().
What is id and type and how is it useful as a programmer?
id() returns the unique identity of an object during its lifetime. This is useful for checking whether two variables are referencing the same object in memory, which is especially important when working with mutable objects.
type() tells you what kind of object you're working with, such as an int, str, list, or dict. Knowing an object's type helps you understand what operations and methods are available and can help you debug type-related errors.
A simple example that demonstrates both id() and type() is:
name = "Gundam"
print(type(name))
print(id(name))Output:
<class 'str'>
777777123654789 # example outputThis shows that name is a string object with its own unique identity in memory.
What are mutable and immutable objects?
Earlier I mentioned mutable objects. This refers to objects whose values can be changed after they are created. Instead of creating a new object, Python modifies the existing object in memory.
Common mutable objects include:
- list
- dictionary (
dict) - set
- bytearray
Example:
arr = [1, 2, 3]
print(id(arr))
arr.append(4)
print(arr)
print(id(arr))Output:
123654789777777
[1, 2, 3, 4]
123654789777777 # same idNotice that the id() stays the same because Python modified the existing list object.
Now we also have immutable objects. These are objects whose values cannot be changed after they are created.
Common immutable objects include:
- integers (
int) - floats (
float) - complex numbers (
complex) - strings (
str) - tuples (
tuple) - frozen sets (
frozenset) - bytes (
bytes) - booleans (
bool)
Example:
x = 10
print(id(x))
x += 1
print(x)
print(id(x))Output:
123654789777777
11
141505352325072 # different objectHere, the id() changes because Python created a new integer object instead of modifying the original one.
Assignment, referencing, and aliases
In Python, variables do not store objects directly. Instead, variables store references to objects in memory.
When you assign one variable to another, both variables can point to the same object.
Example:
original = [1, 2, 3]
copy = original
print(id(original))
print(id(copy))Output:
112343253264364
112343253264364Memory:
original ─────┐
↓
[1, 2, 3]
↑
copy ─────────┘This is called an alias. An alias happens when multiple variables reference the same object.
Because both variables point to the same list, changing one affects the other:
copy.append(4)
print(original)
print(copy)Output:
[1, 2, 3, 4]
[1, 2, 3, 4]Why does it matter and how differently does Python treat mutable and immutable objects?
It matters because mutable objects can be changed in place, which means multiple variables can accidentally affect the same object.
Get Sam’s stories in your inbox
Join Medium for free to get updates from this writer.
When this is not taken into consideration, it can cause unexpected bugs that are difficult to find.
For example, you might try to create a copy of a list like this:
original = [1, 2, 3]
copy = original
copy.append(4)
print(original)
print(copy)Output:
[1, 2, 3, 4]
[1, 2, 3, 4]You might expect only copy to change, but both variables changed because they are pointing to the same list object.
To create a separate object, you need to create a real copy:
original = [1, 2, 3]
copy = original.copy()
copy.append(4)
print(original)
print(copy)Output:
[1, 2, 3]
[1, 2, 3, 4]Immutable objects behave differently. Since immutable objects cannot be changed, Python creates a new object when a new value is assigned.
Example:
name = "John"
print(id(name))
name = name + " Smith"
print(name)
print(id(name))Output:
140625374325040
J Smith
140625374325072Although it looks like we changed the original string, Python created a new string object. The original "" object stayed unchanged.
Memory:
name
↓
"John"
After:
name
↓
"John Smith"
"John" still exists unchangedSpecial case: tuples and frozensets
Although tuples and frozensets are immutable, they can contain mutable objects.
data = ([1, 2], 3)
data[0].append(4)
print(data)Output:
([1, 2, 4], 3)The tuple itself did not change. However, the list inside the tuple changed because the list is mutable.
Integer pre-allocation in Python
Python optimizes memory usage by pre-creating commonly used integer objects.
In CPython, small integers are cached using:
NSMALLPOSINTSNSMALLNEGINTS
These store commonly used numbers so Python can reuse them instead of creating new objects every time.
The reason is simple: integers like 0, 1, 2, and -1 are used extremely often in programs.
Example:
a = 100
b = 100
print(a is b)Output:
TrueBoth variables reference the same integer object because Python reused the cached value.
How are arguments passed to functions and what does this imply for mutable and immutable objects?
In Python, arguments are passed by object reference (also called pass by object sharing).
This means when you pass a variable into a function, Python passes the reference to the object, not a copy of the object.
For mutable objects, changes made inside a function can affect the original object.
Example:
def add_item(items):
items.append("Python")
languages = ["Java", "C"]
add_item(languages)
print(languages)Output:
['Java', 'C', 'Python']The list changed because both the function parameter and the original variable reference the same list.
For immutable objects, the behaviour is different.
def increase(number):
number += 1
value =
increase(value)
print(value)Output:
7The value stays 10 because Python created a new integer object instead of modifying the original object.
The function parameter was assigned a new reference:
Before:
value ───→ 7
number ──→ 7
After:
value ───→ 7
number ──→ 8Understanding how Python passes objects to functions helps you predict whether your data will be modified or remain unchanged.
Conclusion
Understanding Python objects, mutability, references, and how arguments are passed to functions helps you write more predictable and reliable code. Knowing how Python manages objects in memory allows you to avoid unexpected bugs and better understand what happens behind the scenes.





