← Back to blog

Immutable vs Mutable Objects in Python

| Python

This article was published over 2 years ago. Some information may be outdated.

Everything in Python is an object -- integers, booleans, floats, strings, all of it.

Objects fall into two categories: immutable and mutable. Understanding the difference between them is essential to writing correct Python code. This post walks through both concepts by examining how Python manages objects in memory.

Everything is an object

You can verify this yourself:

print( type(1) ) # Output: <class 'int'>
print ( type(1.2) ) # Output: <class 'float'>
print( type("Hello") ) # Output: <class 'str'>

1 is an int, 1.2 is a float, and "Hello" is a str. The same holds true for booleans and every other type.

type() is a built-in function that accepts a single object and returns its type.

Object Identifier

When Python instantiates an object, it allocates a memory address and assigns a unique id at runtime. Once set, this id never changes.

The id() function retrieves this identifier:

a = "Hello"
print( id(a) ) # Output: a random number

The object id is the memory address of the object.

Immutable

Consider this example:

h1 = "Hello"
h2 = "Hello"

print( id(h1) ) # Output: random number like: 4545012936
print( id(h2) ) # as same id(1)

Both h1 and h2 have the same id. They point to the same object in memory.

When "Hello World" is defined, Python gives it a unique id. Every subsequent use of the same string reuses that same memory address:

Python Immutable Objects

Both h1 and h2 reference the same memory-allocated id.

No matter how many times you define "Hello World", you get the same object id:

h1 = "Hello World"
h2 = "Hello World"

print( id(h1) ) # Output: 4368067376 (random number)
print( id(h2) ) # Output: same as id(h1)
print( id("Hello World") ) # Output: same as h1 and h2

"Hello World" has exactly the same id as h1 and h2.

This is what immutable means: the value at a given memory address cannot change at runtime. Python reuses the same object id.

Now consider this:

h1 += ", I love Python"
print(h1) # Output: Hello World, I love Python

It looks like we are appending to h1. We are not. Python creates an entirely new object with the value "Hello World, I love Python" and binds h1 to it:

Python Immutable Objects

Notice that h1 got a new object id.

Behind the scenes, Python creates and manages all objects. "Hello World" is one object; "Hello World, I love Python" is another. The garbage collector reclaims "Hello World" once nothing references it.

Now for the assignment operator. The = operator binds a name to an object -- it does not copy values. name = "Ahmad" binds the name name to the object "Ahmad". That object has its own unique id, which remains constant:

name  = "Ahmad"
name1 = "Ahmad"
name2 = "Ahmad"
name3 = "Ahmad"

# All these statements print the same value
print( id(name) )
print( id(name1) )
print( id(name2) )
print( id(name3) )

Python Immutable Objects

Regardless of how many times "Ahmad" is used, it occupies one address in memory.

All data types in Python are immutable except list, dict, and set.

You may have noticed I have not used the word "variable." That is deliberate.

Python does not have variables in the traditional sense of a box holding a value. It has named references to objects, and these references behave more like labels.
Cite: Python Apprentice by Robert Smallshire and Austin Bingham (https://leanpub.com/python-apprentice) (Chapter 4 -- Built-in types and the object model).

Mutable

With mutable objects, the value can change at runtime without affecting the object id. The memory address stays the same.

Here is an example where we append to a list:

s1 = [1, 2, 3, 4]
s2 = s1
s2.append(5)
print(s1) # Output: [1, 2, 3, 4, 5]

Why does s1 include the appended value? Because s1 and s2 point to the same object:

Python Immutable Objects

s2 has the same id as s1.

Changing s2 changes the underlying object, which means s1 sees the change too:

Python Immutable Objects

s1 and s2 are using the exact same object.

However, when you create two separate lists with the same values, they get different ids:

s1 = [1, 2, 3, 4]
s2 = [1, 2, 3, 4]

# s1 and s1 have different reference ids
print( id(s1) )
print( id(s2) )

This is the core difference from immutable types: two identical immutable values share an id, while two identical mutable values do not.

Equality of value vs equality of identity

The is operator tests whether two references point to the same object (identity equality):

a = "Hello"
b = "Hello"
print( a is b ) # Output: True

This is True because "Hello" is immutable and Python reuses the same object.

The == operator tests whether two values are equal, regardless of identity:

s1 = [1, 2, 3, 4]
s2 = [1, 2, 3, 4]

print( s1 is s2 ) # Output: False
print( s1 == s2 ) # Output: True

Since list is mutable, each list gets its own id. s1 is s2 is False because they are different objects. s1 == s2 is True because their values are identical.

Summary

  • All Python types are immutable except list, dict, and set -- these three are mutable.
  • id() returns the memory address of an object, which never changes for immutable types.
  • type() returns the data type of a given object.
  • is tests identity equality -- whether two names reference the same object in memory.
  • == tests value equality -- whether two objects hold the same data, regardless of identity.
  • The assignment operator (=) binds a name to an object -- it does not copy values. Multiple names bound to the same immutable value share one memory address.
Share