Introduction
If you are new to programming you will often hear these two terms mutable and immutable objects. If you are still in confusion or if you want to understand more about mutability & immutability then this article is for you.
In Python, everything is an object. Every object has its own data type and internal state (data). Let us first try to understand how data is stored in the memory with below two examples.
Example 1: When Python executes a = 1000
, this creates an integer object at some memory location say 0x1111
& stores value 1000
. Here a
doesn’t contain value 1000
but just holds the reference (memory address) to this object. When the next statement a=1500
is executed, another integer object is created at memory location 0x2222
with value 1500
and a
now references this new object.
a = 1000
a = 1500
Example 2: When Python executes a = 2000
, as explained in the above example, it creates an integer object at memory location 0x3333
and a
will now point to this object. After the execution of b = a
, now b
also points to the same memory location a
is referring to. Note that the value of a
is not copied to b
but the reference (address) is copied to b
.
a = 2000
b = a
id() function
Everything is an object in Python. Each object is stored at some memory location. The id()
function returns the memory location of the object referenced by a variable. This id()
function returns address in base-10 number. We can convert this to hexadecimal or binary. So this function can be used to check if the objects are pointing to same memory location in other words the same data in the memory.
a = 10
print(id(a))
>>> 2574450501768
print(hex(id(a))
>>> 0x25769352888
print(bin(id(a))
>>> 0b100101011101101001001101010010100010001000
is keyword
is
keyword compare two objects and return True if both the objects are pointing to the same memory address else return False. This is not the same as the equality (==)
operator. The equality operator compares the actual value stored in the object.
a = 75
b = a
print(id(a))
>>> 140713752963792
print(id(b))
>>> 140713752963792
print(a is b)
>>> True
Mutable and Immutable Objects
An object whose internal state (data) can be changed is called a Mutable object. Similarly, an object whose internal state (data) can not be changed is called an Immutable object. It is as simple as that.
Mutable
- Lists
- Sets
- Dictionaries
Since lists, sets & dictionaries are mutable, we can add, delete & modify its contents. In all the three examples below, the memory location did not get changed as expected during the operation. This confirms that lists, sets & dictionaries are mutable objects.
List example
my_list = [1,2,3]
print(my_list)
print(id(my_list))
>>> [1,2,3]
>>> 1863010210504
my_list.append(4)
print(my_list)
print(id(my_list))
>>> [1,2,3,4]
>>> 1863010210504
Set example
my_set = {1,2,3}
print(my_set)
print(id(my_set))
>>> {1, 2, 3}
>>> 2688892018248
my_set.add(4)
print(my_set)
print(id(my_set))
>>> {1, 2, 3}
>>> 2688892018248
Dictionary example
my_dict = {‘key1’: 10, ‘key2’: 20, ‘key3’:30}
print(my_dict)
print(id(my_dict))
>>> {'key1': 10, 'key2': 20, 'key3': 30}
>>> 2688890813480
my_dict.update({‘key4’: 40})
print(my_dict)
print(id(my_dict))
>>> {'key1': 10, 'key2': 20, 'key3': 30, 'key4': 40}
>>> 2688890813480
Immutable:
- Numbers (int, float, decimal, boolean etc.)
- Strings
- Tuples
- Frozen Sets
Since numbers, strings, tuples & frozen sets are immutable, internal state (data) can not be changed. In all the examples below, the memory location (address) gets changed because a new object gets created at different memory location during the operation. This confirms that numbers, strings, tuples and frozen sets are immutable.
Number example
n = 10
print(n)
print(id(n))
>>> 10
>>> 140713425216176
n = n + 10
print(n)
print(id(n))
>>> 20
>>> 140713425216496
String example
s = "Python"
print(s)
print(id(s))
>>> Python
>>> 2689033043056
s = s + " Simplified"
print(s)
print(id(s))
>>> Python Simplified
>>> 2689033474496
Tuple example
my_tuple = (10,20,30)
print(my_tuple)
print(id(my_tuple))
>>> (10, 20, 30)
>>> 2689034125544
my_tuple = my_tuple + (40,)
print(my_tuple)
print(id(my_tuple))
>>> (10, 20, 30, 40)
>>> 2689034073256
Frozen set example
a = frozenset((1, 2))
print(a)
print(id(a))
>>> frozenset({1, 2})
>>> 2689035141416
a = a.union(frozenset((3, 4)))
print(a)
print(id(a))
>>> frozenset({1, 2, 3, 4})
>>> 2689035142536
In Python, by default, objects are passed to function ‘by reference’. So one should be careful when mutable/immutable objects are passed to function call to avoid unintended side-effects. Let’s look at two examples:
In the example below, list_1
is a reference to an object in the memory. When the function my_list is called list_1
is passed as a reference. In the function scope, this creates another object lst
which also references to the same memory location as lists are mutable. Any changes made to lst
in the function scope gets reflected in list_1
as well. If this is not we want then we need to make necessary changes in the function so that changes made in the function don’t get reflected in the list_1
. This can be confirmed by looking at the output of the id
function.
def my_list(lst):
print(‘id before append: {0}’.format(id(lst)))
lst.append(40)
print(‘id after append: {0}’.format(id(lst)))
list_1 = [10,20,30]
print(‘id before calling function:’, id(list_1))
print(my_list(list_1))
id before calling function: 2689035427464
id before append: 2689035427464
id after append: 2689035427464
In this example, since strings are immutable objects, s = s + ‘Towards AI’
will create a new object in the memory but it will point to a different memory location. This can be confirmed by looking at the output of the id
function.
def my_string(s):
print(‘id before append: {0}’.format(id(s)))
s = s + ‘Towards AI’
print(‘id after append: {0}’.format(id(s)))
string_1 = ‘Hello ‘
print(‘id before calling function’, id(string_1))
my_string(string_1)
id before calling function 2689036049328
id before append: 2689036049328
id after append: 2689035600160
Even though tuples are immutable, elements of a tuple can be mutable. Let’s look at an example:
In this example, t
is an immutable object (tuple) but its elements list_1
& list_2
are mutable objects. This is perfectly fine as lists are mutable their states can be modified.
list_1 = [10, 20]
list_2 = [40, 50]
t = (list_1, list_2)
print(t)
>>> ([10, 20], [40, 50])
list_1.append(30)
list_2.append(60)
print(t)
>>> ([10, 20, 30], [40, 50, 60])
Conclusion
In this article, we talked about mutability and immutability in Python with easy-to-understand examples. We hope that you are now clear about what is mutability and immutability in Python. If you still have any questions, please let us know the comments.
References
Originally published at Medium on 6-Aug2020