Python Mutability Explained

Matrix movie still

Python mutability refers to being able to change an object. Simply put, a mutable object can be changed, but an immutable object cannot.

For example, a tuple is an immutable data type. You cannot change its elements:

nums = (1, 2, 3)
nums[0] = 100 # ERROR!

In contrast, a list is a mutable collection. You can change its contents afterward:

nums = [1, 2, 3]
nums[0] = 100 # Works

How to Understand Python Mutability

Everything in Python is an object. Each object falls into the category of being mutable or immutable.

When you create a Python object it gets a unique object id under the hood. Depending on whether the type is mutable or immutable, its state can be changed.

Here are some common data types and the related mutability:

TypeMutableImmutable
boolx
intx
floatx
strx
listx
tuplex
setx
frozensetx
dictx

But what does immutability really mean behind the scenes in Python? How could you find out whether something is mutable or not? And how come is an int is an immutable type? You can change an integer, can’t you?

To figure out the answers to these questions, please read along.

Before taking a deeper look at Python mutability, you need to learn what the built-in id() function does.

The id() Function in Python

Each and every Python object has a unique ID that is related to the memory location of a Python object.

The id() function returns this object ID.

For instance:

word = "This is an example"

print(id(word))

Output:

4557539968

This shows you that the id is a random integer value that represents the memory location of the string.

Mutability in Python

A mutable object can change but an immutable object can’t.

Now, let’s spot if a Python object is mutable or immutable by nature. As you know, an integer is immutable. But let me show how you could verify this.

For example, let’s create two integers:

a = 1
b = a

Now both a and b point to the same memory address. In other words, the id() function should return the same ID for both integers:

a = 1
b = a

print(id(a) == id(b))

Output:

True

And this is indeed the case.

But now, let’s change the value of a:

a = 10

Now, let’s compare the IDs of a and b again:

print(id(a) == id(b))

Output:

False

The IDs don’t match anymore. This is because now a points to a different integer object. In other words, the integer object 1 itself never changed. But the variable a that pointed to it now points to another integer object called 10.

This result suggests that the integer is immutable by its nature, which is indeed the case.

If this is hard to understand, think about it this way: 1 is just a name of an immutable integer object. And 10 is a name of a different immutable integer object. You cannot change these integer objects. 1 is always 1 and 10 is always 10. But you can make a variable point to these different integer objects.

Now, let’s repeat a similar experiment with a mutable object.

For example, let’s create a list:

nums = [1, 2, 3]
l = nums

Now, let’s compare the IDs of the lists:

print(id(nums) == id(l))

Output:

True

Now, let’s modify the list object by removing its first element:

del nums[0]

Let’s then check if the IDs of the two lists still match:

print(id(nums) == id(l))

Result:

True

And they do! This means nums and l still point to the same list object in memory.

To make the point clear, let’s print the contents of nums and l:

print(nums)
print(l)

Output:

[2, 3]
[2, 3]

This shows that by removing the first element you were clearly able to directly modify the list object. Thus, the list object must be mutable. And this is indeed the case in Python.

To Recap

  • In the integer’s case, the integer object itself did not change because it is immutable. Instead, the variable started pointing to a different integer object in memory.
  • But a mutable list object can change. So its memory address does not change when you modify it. This is because even though you change how it looks, it is still the very same list object under the hood.

“Exceptions” in Immutability

Now that you understand mutability in Python, let’s have a look at an example that can cause confusion.

In Python, tuples are immutable data types. This means once you create a tuple, you cannot replace, add, or remove its elements:

nums = (1, 2, 3)
nums[0] = 100 # ERROR!

But here is where it gets interesting. If you store a list (mutable type) into a tuple, you can still change the contents of that list.

For instance:

nums = (1, 2, 3, [10, 20, 30])

#Change the first value of the list in the tuple from 1 to 1000:
nums[3][0] = 1000

print(nums)

Output:

(1, 2, 3, [1000, 20, 30])

Here you can see how the first value of the list inside the tuple changed from 1 to 1000.

But how is this possible? A tuple is immutable, so it should not change, right?

The value of a tuple cannot be changed after it is created. But in reality, a value of a tuple is just a name that is bound to an object behind the scenes. These bindings are immutable, but not the objects they are bound to.

In other words, if a tuple element points to a mutable list, you can modify the elements of the list. But you cannot replace the list with another item in the tuple. This would break an unbreakable binding.

So the value of an immutable object can not change. But the object it points to can change (if it is mutable).

Conclusion

Python mutability means an object’s ability to change. An immutable object cannot change, but a mutable object can.

For example, a list is mutable, but a tuple is not. In other words, you can freely change the elements of a list, but not the ones of a tuple.

Further Reading

Shallow Copy vs Deep Copy in Python

Share on facebook
Facebook
Share on google
Google+
Share on twitter
Twitter
Share on linkedin
LinkedIn
Share on pinterest
Pinterest

Leave a Comment

Your email address will not be published. Required fields are marked *