Mutability refers to the ability to change or be changed.

Objects in Python are classified as either mutable or immutable depending on their ability to be modified after they are created. 

Immutable objects are objects whose  state cannot be modified once created. Some examples of immutable core types includes tuples, strings, and numbers(ints, floats and complex) 

Mutable objects,  on the other hand,  are those whose internal state can be modified after they are created, this means that a mutable object can have different values at different points in time.Some examples of mutable core types includes lists, sets and dictionaries.  

Immutable Objects

When an object is immutable, we cannot modify any of its contents. Thus once, it has been created, an immutable objects becomes a read-only, we can only access its contents but not modify them. 

Operations done to immutable objects tends to create new objects with the modified contents of the original object(s) as opposed to altering the original object.

S1 = 'Python'

#creates a new string with the contents of S uppercased.
S2 = S1.upper()

print(S2)

In immutable sequences such as strings and tuples, index assignment is not allowed.

T = (1, 2, 3, 4)

#raises an error
T[0] = 5

Immutable objects are hashable

A hashable element is an objects that can be used as a dictionary key or as an element in a set. Typically,  immutable objects in Python are hashable. 

#immutable elements are allowd in sets
S = {1, (1, 2, 3), 'abc'}

print(S)

#immutable elements can be used as dictionary keys
D = {(1, 2): 'Python', (3, 4): 'Java' }

print(D[(1, 2)])

Advantages of immutable data types

Generally speaking, immutable objects are more memory efficient compared to  mutable objects. Immutable objects are essentially constant and can, therefore, be copied and passed around without incurring the cost of extra memory usage.

Immutable objects ensures data integrity in that any changes made to the object must create a new object, making it impossible for the original object to be changed.

Since immutable objects are read-only,  they ensures thread safety as multiple threads can access them without having to worry about any changes made by other threads.

Mutable objects

When an object is mutable, we can change its contents, modify its attributes, add or delete items from it, and so on. Operations done on the object tends to affect the object itself, as opposed to creating a new object with the modified contents.  Lists, dictionaries and sets are mutable objects . 

L = [1, 2, 3, 4]

#modify the list
L.append(10)
L.append(20)
L.append(30)

print(L)

In mutable sequences such as the list, it is possible to perform index assignment operations. This also means that we can perform some important operations such as sorting the list in-place.

L = [1, 4, 0, 3, 2]

#sort the original list in-place
L.sort()

print(L)

Mutable objects are not hashable and cannot be used as dictionary keys and set elements.

sets and dictionaries

A set is a mutable data type which is itself made of immutable elements. Due to their mutable nature, we can perform set operations  in-place.:   

my_set={1,2,3,4,5} 

# to add a new element to the set 
my_set.add(6) 

my_set.remove(3)

print(my_set)

With dictionaries, we can perform assignments, deletions and other operations without having to create another dictionary.

my_dict = {'one': 1, 'two': 2, 'three': 3}

#mpodify the dictionary
my_dict['four'] = 4
my_dict['five'] = 5

print(my_dict)
 

Advantages of mutable objects

  • Mutable objects allow changes to be made to the object in-place, without the need to create a new object. This is memory-efficient especially when large amounts of data is being worked on.
  • Mutable objects can be efficiently passed as arguments to a function. Because a reference to the same object is passed, the function can modify the object in-place without requiring a copy to be created.
  • Mutable objects can be used to represent dynamic data that is meant to change over time.   

Mutable vs immutable objects

mutable immutable
Can be modified after they are created. Cannot be modified once created
Operations tends to modify the original object rather create new objects. Operations tends to produce new objects rather then modifying the original object.
Are typically not hashable. Are usually hashable and can therefore be used as dictionary keys and set elements.
Are not regarded as  thread safe. They are thread safe.
Mutable core types include list, dictionaries and sets Immutable core types include strings, tuples and numbers.

Immutable objects made of mutable elements

When an immutable object is made up of mutable elements, some operations may make it seem like we are modifying the object. This is because, an object being immutable does not prevent the elements inside it from being modified.

Consider the following example.

data = ([1, 2], [3, 4], [5, 6])

data[0][0], data[0][1] = 100, 200

print(data)

You should note that in the above case, we did not really modify the tuple itself but rather the  element at index 0 which happens to be mutable.