Fundamentals 11 min read

Why Everything Is an Object in Python: Understanding Mutable vs Immutable

This article explains Python’s core principle that every value is an object, detailing how identity, type, and value define objects, and contrasts mutable and immutable types through practical code examples, illustrating their impact on memory management, variable assignment, function arguments, and common pitfalls.

Code Mala Tang
Code Mala Tang
Code Mala Tang
Why Everything Is an Object in Python: Understanding Mutable vs Immutable

When I started learning Python I was surprised by the statement “everything is an object”. In Python every value has three attributes: an identity (its memory address), a type, and a value. The built‑in functions id() and type() reveal the first two.

<code>a = 42
b = "Hello, Python!"
c = [1, 2, 3]

print(f"a: id={id(a)}, type={type(a)}")
print(f"b: id={id(b)}, type={type(b)}")
print(f"c: id={id(c)}, type={type(c)}")
</code>

Running the code shows that each object has a distinct id and a type such as int , str , or list . The operator is compares identities, while == compares values.

<code>a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(f"a == b: {a == b}")   # True, values are equal
print(f"a is b: {a is b}")   # False, different objects
print(f"a is c: {a is c}")   # True, same object
</code>

Mutable Objects

A mutable object can change its value without changing its identity. Lists, dictionaries and sets are examples.

<code>my_list = [1, 2, 3]
print(f"Original list: {my_list}, id={id(my_list)}")

my_list.append(4)
print(f"After append: {my_list}, id={id(my_list)}")

list_copy = my_list
list_copy.append(5)
print(f"Original after modifying copy: {my_list}, id={id(my_list)}")
print(f"Copy: {list_copy}, id={id(list_copy)}")
</code>

The output demonstrates that the list’s id stays the same while its contents change, and every reference sees the modification.

Immutable Objects

Immutable objects cannot be altered after creation. Integers, floats, strings and tuples belong to this group.

<code>s = "hello"
print(f"Original string: {s}, id={id(s)}")

s = s + " world"
print(f"After concatenation: {s}, id={id(s)}")

a = 42
b = 42
print(f"a: {a}, id={id(a)}")
print(f"b: {b}, id={id(b)}")

str1 = "Python"
str2 = "Python"
print(f"str1 is str2: {str1 is str2}")
</code>

Appending to a string creates a new object with a new id . Small integers and short strings are cached, so identical literals may share the same object.

Why Mutability Matters: Python’s Different Handling

1. Assignment behavior

<code>x = 10
y = x
x = 20
print(f"x: {x}, y: {y}")

list_a = [1, 2, 3]
list_b = list_a
list_a[0] = 99
print(f"list_a: {list_a}, list_b: {list_b}")
</code>

Rebinding an immutable variable creates a new object, while mutating a mutable object affects all references.

2. Performance considerations

Immutable objects are predictable, thread‑safe and can be used as dictionary keys.

<code>tuple_key = (1, 2)
list_value = [3, 4]

dict_example = {tuple_key: "Example with tuple key"}
print(dict_example)

try:
    dict_example = {list_value: "This will fail"}
except TypeError as e:
    print(f"Error: {e}")
</code>

3. Memory management and efficiency

Because immutable objects never change, Python can reuse them, saving memory. Examples include the small‑integer cache (‑5 to 256) and string interning.

<code>a = 256
b = 256
print(f"a is b (small integers): {a is b}")

c = 257
d = 257
print(f"c is d (larger integers): {c is d}")

s1 = "python"
s2 = "python"
print(f"s1 is s2: {s1 is s2}")
</code>

Passing Arguments to Functions

Function arguments are passed by object reference. The effect differs for mutable and immutable objects.

<code>def modify_immutable(n):
    n += 1
    print(f"Inside function: n = {n}, id={id(n)}")

num = 10
print(f"Before function call: num = {num}, id={id(num)}")
modify_immutable(num)
print(f"After function call: num = {num}, id={id(num)}")
</code>
<code>def modify_mutable(lst):
    lst.append(4)
    print(f"Inside function: lst = {lst}, id={id(lst)}")

my_list = [1, 2, 3]
print(f"Before function call: my_list = {my_list}, id={id(my_list)}")
modify_mutable(my_list)
print(f"After function call: my_list = {my_list}, id={id(my_list)}")
</code>

To avoid unintended changes, make a copy inside the function.

<code>def safe_modify(lst):
    local_lst = lst.copy()
    local_lst.append(4)
    return local_lst

my_list = [1, 2, 3]
print(f"Original list: {my_list}")
new_list = safe_modify(my_list)
print(f"Original list after function: {my_list}")
print(f"New list returned by function: {new_list}")
</code>

Summary

Everything in Python is an object with identity, type and value.

Mutable objects can be changed in place; all references see the change.

Immutable objects cannot be changed; operations produce new objects.

Python reuses immutable objects via interning and caching to save memory.

Function arguments are passed by reference, so mutability influences whether a function can modify the caller’s data.

Memory ManagementPythonImmutablefunction-argumentsobjectsMutable
Code Mala Tang
Written by

Code Mala Tang

Read source code together, write articles together, and enjoy spicy hot pot together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.