Python immutable objects are not what you think
Table of Contents
All data in a Python program are represented by objects or by relation between objects. Every object has 3 things; an identity, a type and a value.
According to the documentation of Python, we may think of an object’s identity as the object’s address in memory. However, The real meaning of an object’s ID is implementation dependent. In CPython, id() returns the memory address of the object, but it may be something else in another Python interpreter. The key point is that the ID is guaranteed to be a unique integer label, and it will never change during the life of the object. An object’s type determines the operation that the object supports and it also defines the domain of values that the object can hold. Finally, we consider an object to be mutable if its value can change and immutable if its value cannot change. But taking a closer look at some of Python’s operations, we will see that it is not that simple.
Immutable objects #
An object’s mutability is determined by its type; for instance, numbers, strings and tuples are immutable, while dictionaries and lists are mutable.
Now Let’s look at the following code snippet:
my_tuple = (1,2, [3,4])
my_tuple[2].append(5)
print(my_tuple)
Tuples in python are immutable objects. However, the code above does not raise an error. This is because the tuple is immutable but the list inside the tuple is not. The list is mutable and can be changed. The tuple itself cannot be changed but the list inside the tuple can be changed. The snippet will output the following:
(1, 2, [3, 4, 5])
A nifty online tool that can help us understand how Python works behind the scenes is called Python Tutor. It allows us to visualize the execution of Python code. The following figure shows the execution of the code above:
What immutability really means in Python #
In general, the value of an immutable container object that contains a reference to a mutable object can change when the latter’s value is changed; however the container is still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle. Objects whose values can never change are called hashable. Let’s take a look at the following code snippet to understand the difference:
t1 = (1, 2)
hash(t1) # returns the hash of t
t2 = (1, 2, [3, 4])
hash(t2) # raises an error because the list inside the tuple is mutable
The tuple t1 is hashable because it is immutable. The tuple t2 is not hashable because the list inside the tuple is mutable. The hash of an object is an integer that is used to quickly compare dictionary keys during a dictionary lookup. Hashable objects which compare equal must have the same hash value. This is why the hash of t2 cannot be computed because the list inside the tuple is mutable.
Conclusion #
To conclude, we can say that immutability in Python is not what we think it is. It is more subtle than that. An object is immutable if its value cannot change. However, an object is hashable if its value cannot change and it does not contain any mutable object. To play it safe, it is also considered best practice to avoid putting mutable objects inside immutable containers.