I hail from a C/C++ programming background. It was a bit tough for me to get hold of Python at first. It was mostly because I couldn’t understand Python’s object model and immutability. Now that I understand them, I thought of posting an explaination here.
What are immutable objects?
In Python everything is an object – the data types, the containers, the functions and everything else. This means that these things have methods, functions, attributes and other properties associated with them.
These objects can be classified as mutable or immutable. Mutable objects can be modified and their values can be changed. Immutables are opposite. They can’t be modified and they retain their identity. I will clarify these things in a while.
Following object types are immutable:
- numeric types: int, float, and complex
These are mutable ones:
The Real Thing
Let’s define an object
x = 5, which is a numeric type and hence must be immutable. But we can surely run the statement
x = 7 and the value of
x changes successfully. So where’s immutability?
Here, you must convince yourself that
x is not the actual object. It is merely a name which is bind to (or points to) the actual numeric object
5. When we run the statement
x = 7, we are essentially creating a new object
7 in the objectspace, and binding (assigning) it to
x which is present in the namespace. Assignment in Python is merely a binding.
A similar thing happens with strings.
We can define a string object
s = 'prosoy'. If we try
s = 't', we get an error because the object bind to
s is immutable. You cannot change an immutable object.
But we can definitely do
s += ' granatu' and now
'prosoy granatu'. Here, we concatenated the pre-existing object
'prosoy' with the newly created object
' granatu' , which resulted in creation of yet another new object
s was then bind to this resulting object.
Note that none of these three string objects were modified in the process.
Impact of Immutability
Another important thing to note is that in a string concatenation operation, we are creating two new string objects. So, iterative concatenation of large number of strings can be memory-wise very expensive.
#Let str_container be a container of string objects
#Method 1: Iterative Concatenation of strings. Not recommended.
str1 = ''
for data in string_container:
str1 += data #expensive
#Method2: More pythonic. Recommended.
str1 = ''.join(string_container)
Deeper Into Object Model
Variables in Python behave differently from traditional languages like C++.
In C++, a variable name acts as a container of value. These variable names are associated with a fixed memory address where the value of variable is stored. When a new value is assigned to the variable, the older value is replaced with the new one. The memory address still remains the same. This memory address acts as a container. The variable actually contains the value.
using namespace std;
int a = 5;
cout << a << ", " << &a << endl; //Prints 5, 0x7ffd991585d4
a = 8;
cout << a << ", " << &a << endl; //Prints 8, 0x7ffd991585d4
// Address of the variable remains same
Python’s object model is different. The variables don’t work that way. The assignment doesn’t work that way. Variable names do not contain values. They are just names referring to actual objects in memory. The assignment only represents that reference (bind).
I will illustrate this in the following code. We will use in-built function
id() which returns the decimal memory address of object. The function
hex() will convert the decimal to hexadecimal.
>>> x = 5
>>> x = 10
Note how the address of
x changes on reassignment but the object
5 is still in its own place. Even if we assign several other variables to
5, they all would be referring to the same memory location. In this way Python avoids the duplication of immutable objects in memory. This makes sense too! If the objects are immutable and can’t be modified, then we must avoid duplication of those objects to save memory.
However, this is not the case with mutable objects. Two ‘similar’ mutable objects can have different memory locations. This is because while modifying an object, we do not want to unintentionally reflect changes to all other similar objects.
But if multiple variables refer to the ‘same’ mutable object, then changing one of them will reflect change on others too. The following code clarifies this.
>>> # Immutables below
>>> a = 6
>>> b = 6
>>> a is b
>>> # Mutables below
>>> # list1 and list2 below are similar but not same.
>>> list1 = [2,4,6]
>>> list2 = [2,4,6]
>>> list1 is list2
>>> # Let's change list1 to remove 'similarity'
>>> list1.append(8) #mutating list1
[2, 4, 6, 8]
[2, 4, 6]
>>> # Now Let's make both lists 'same'
>>> list2 = list1 # list1 and list2 refer to same object
>>> list2.append(10) # this mutates both list1 and list2
[2, 4, 6, 8, 10]
[2, 4, 6, 8, 10]
Thank you for reading through. If you have any query related to the post feel free to comment.