Why do I care about immutables in Python?
Very often we read about mutable data types and immutable data types. For Example, Strings are immutable in Java. There are multiple resources out there that talks about this concept, but I couldn’t find a complete discussion with respect to Python. In this article, I would discuss this concept. Its use case and why we need immutability with respect to Python?
Immutable Object
An immutable object is an object that cannot be modified. That means once created its state cannot be changed. It will represent the same value once created. For Example — String is immutable in python. A string object will always represent the same value, it can’t be modified. For example — The string object ‘h’ will always represent the python value h. No matter what happens!
Another Example — If there is a string object like ‘hello’ and I want to change the character at the second position that is ‘e’ to ‘o’ to make the string object ‘hollo’. There is no way, I can do this in python because strings are immutable in Python. The only solution is to make another new string object ‘hollo’.
Let us verify this in python.
name = 'python'
name[0] = 'z' #This won't change the original string to zython.
#This will result in an error as item assignment
#is not supported for strings. Even if we implement
#item assignment, we will have to create a new
#string.
Another Example:
x = 'hello'
print(id(x)) #This will print some id like 8879437848.
x = 'python'
print(id(x)) #This will print some id like 4387438948 different
# from the previous one.
Since the string object ‘hello’ is different from the string object ‘python’. The function id returns different unique ids. This implies that the memory location where these objects are stored is different and these are two different objects.
Mutable Object
A mutable object is an object whose state can be modified after it is created. It may or may not represent the same value during the execution of the program. For Example — A list is a mutable object.
myList = [1 , 2 , 3]
print(id(myList)) #This will print id like 8594375837.
myList.append(5) #This appends 5 to the list resulting in
# myList to be[1, 2, 3, 5]
print(id(myList)) #This will print id with the same value as
# before 8594375837.
Here, we find that both ids came out to be equal. Because when we append the list with 100 or tried to modify the original list. No new list object is created rather the original list is modified as it is a mutable data type.
In fact, Integers, Floats, and Tuples are also immutable data types in python. List, sets, dictionary are mutable data types. Also, there are no primitives in python. Everything is an object.
Do I really need immutables?
Why do we need immutability?
- Immutable objects are thread safe. The state of an object cannot be changed after its construction. This implies that read-only data is shared among threads, which provides thread safety. Operations involving mutations like (changing value at a particular index) can be implemented in such a way that they create new objects instead of modifying the existing ones.
- Immutable objects improve correctness and clarity. It provides a sane way to reason about the code. As the value of the object won’t change throughout the execution of the program. We can freely pass around objects without the fear of modification.
- Mutable objects are much harder to reason about. Mutable objects with multiple references pointing to the same object also called as aliasing leads to inconsistencies and threatens correctness of the code. One part of code mutating the value and another part of code affected due to this mutation. This is why it results in inconsistency.
Python also has some optimizations around this immutability. Especially, for string objects. If we create 10 string objects with the same value, then it won’t allocate memory for 10 strings rather every identifier would refer to the same string. Thus, saving memory. Although, this thing is implementation dependent and won’t work exactly like this for other immutable data types.
Example:
myList = [] #Create an empty list.
for i in range(10):
myList.append('hello') #Append 'hello' 10 times.
for i in range(10):
print(id(myList[i])) #id of each item will be same because
# they all refer to the same object.