Python Simplified

Mutability & Immutability in Python

Mutability & Immutability in Python resized

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
				
			
mutability1

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
				
			
mutability2

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

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:

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.

Originally published at Medium on 6-Aug2020

Share on facebook
Share on twitter
Share on linkedin
Share on whatsapp
Share on email
Chetan Ambi

Chetan Ambi

A Software Engineer & Team Lead with over 10+ years of IT experience, a Technical Blogger with a passion for cutting edge technology. Currently working in the field of Python, Machine Learning & Data Science. Chetan Ambi holds a Bachelor of Engineering Degree in Computer Science.
Scroll to Top