How to solve the immutability helper problem in Python
In programming, immutability refers to an object whose state can not be changed after creation. Python doesn’t have any built-in functionality to support immutability. We can try to achieve this by:
Using
forzenset()Using custom classes
Using
namedtuple()
Using frozenset()
The frozenset(iterable) is an immutable version of a Python set. Here, iterable can be a tuple, dictionary, or set. Consider the example below.
person = {"name": "Anne", "age": 23}person["age"]=24# Immutable iterable created from dictionaryimm_dict = frozenset(person)print(imm_dict)
Explanation
Lines 1–2: We create a simple dictionary,
person, with two keys. In line 2, we change the value ofageto 24. This should work, as a Python dictionary is a mutable iterable.Lines 5–6: We create a frozen set
imm_dictvia thepersondictionary. Notice the output; it only takes the keys of the dictionary to create the frozen (immutable) set.
Another example with the set object is as follows:
odd = {1, 3, 5, 7, 9}odd.add(11)print(odd)# Immutable iterable created from setimm_set = frozenset(odd)imm_set.add(11) # Error expected
Explanation
Lines 1–3: We create a simple set,
odd, which contains odd numbers from 1 to 9. Next, we add 11 to the set using theadd()function. Theprint()(line 3) verifies that a new element was added.Lines 6–7: We create a frozen set
imm_setvia theoddset. Line 7 shows an expected error because, after creation, we can not add any new element in theimm_setobject which is a frozen (immutable) set.
Using custom classes
To achieve immutability, we can create our class with read-only properties. Consider an example below.
# Custom class with read-only propertiesclass Immutable:def __init__(self, obj1, obj2):self._obj1 = obj1self._obj2 = obj2@propertydef obj1(self): # Getter for obj1return self._obj1@propertydef obj2(self): # Getter for obj2return self._obj2# Mainimmutable = Immutable(4, 3)print(immutable.obj1) # Reading obj1print(immutable.obj2) # Reading obj2
Explanation
Lines: 3–5: The constructor implies that every object of
Immutabletype will have two attributes:obj1andobj2.Lines 7–9: The
@propertydecorator is simply a Pythonic way to use getters/setters. Here,obj1(self)is a getter that returns the value ofobj1.Lines 11–13: Here,
obj2(self)is a getter that returns the value ofobj2.Lines 17–19: We create an object of
Immutabletype,immutable. Next, we simply call the getters onobj1andobj2, which print 4 and 3, respectively.
Note: There is no way to change the values of attributes, after an object is created.
Using namedtuple()
The namedtuple() is a lightweight way to create an immutable object without creating a custom class. This function creates a simple class with named attributes. Below is an example:
# Import from collections modulefrom collections import namedtuple# Creating a namedtupleImmutable = namedtuple('Immutable', ['obj1', 'obj2'])immutable= Immutable(obj1=4, obj2=3) # Object created with type immutable (namedtuple)print(immutable.obj1)print(immutable.obj2)#immutable.obj1=5 # Error expected
Try uncommenting line 11. Notice the error because we can not change the attributes after creation.
Explanation
Line 1: We import the
namedtuple()function and thecollectionsmodule.Line 5–6: We create a
namedtuplecalledImmutablewith two fields:obj1andobj2. Next, we create an object,immutableofImmutabletype and assign values to fields.Line 8–9: The
namedtupletype objects are hashable. We print the values ofimmutablefields,obj1andobj2, which print 4 and 3, respectively.
Free Resources