4. Creating Immutable Classes In Python
By Bernd Klein. Last modified: 19 Feb 2024.
Why do we need immutable classes?
Popular examples of immutable classes in Python include integers, floats, strings and tuples. Many functional programming languages, such as Haskell or Scala, heavily rely on immutability as a fundamental concept in their design. The reason is that immutable classes offer several advantages in software development:
-
Thread Safety: Immutable objects are inherently thread-safe. Since their state cannot be changed after creation, multiple threads can access them concurrently without the need for locks or synchronization. This can simplify concurrent programming and reduce the risk of race conditions.
-
Predictable Behavior: Once an immutable object is created, its state remains constant throughout its lifetime. This predictability makes it easier to reason about the behavior of the object, leading to more robust and maintainable code.
-
Cacheability: Immutable objects can be safely cached, as their values never change. This is particularly beneficial for performance, as it allows for efficient memoization and caching strategies.
-
Simplifies Testing: Since the state of an immutable object doesn't change, testing becomes simpler. You don't need to consider different states or mutation scenarios, making it easier to write tests and verify the correctness of your code.
-
Consistent Hashing: Immutable objects have consistent hash codes, which is essential for their use in data structures like dictionaries or sets. This ensures that objects with the same values produce the same hash code, simplifying their use in hash-based collections.
-
Facilitates Debugging: Debugging can be easier with immutable objects because their state doesn't change. Once you identify the initial state of an object, it remains constant, making it easier to trace and understand the flow of your program.
-
Promotes Functional Programming: Immutable objects align well with the principles of functional programming. In functional programming, functions and data are treated as separate entities, and immutability is a key concept. Immutable objects encourage a functional style of programming, leading to more modular and composable code.
-
Prevents Unintended Changes: With mutable objects, unintended changes to the state may occur if references to the object are shared. Immutable objects eliminate this risk, as their state cannot be modified after creation.
Live Python training
See our Python training courses
Ways to Create Immutable Classes
Classes with Getters and no Setters
The following ImmutableRobot class implements private attributes __name and self.__brandname, which can only be read through the methods get_name and get_brandname but there is no way to change these attributes, at least no legal way:
class ImmutableRobot:
def __init__(self, name, brandname):
self.__name = name
self.__brandname = brandname
def get_name(self):
return self.__name
def get_brandname(self):
return self.__brandname
robot = ImmutableRobot(name="RoboX", brandname="TechBot")
print(robot.get_name())
print(robot.get_brandname())
OUTPUT:
RoboX TechBot
We can rewrite the previous example by using properties and not suppling the setter methods. So logically the same as before:
class ImmutableRobot:
def __init__(self, name, brandname):
self.__name = name
self.__brandname = brandname
@property
def name(self):
return self.__name
@property
def brandname(self):
return self.__brandname
robot = ImmutableRobot(name="RoboX", brandname="TechBot")
print(robot.name)
print(robot.brandname)
try:
robot.name = "RoboY"
except AttributeError as e:
print(e)
try:
robot.brandname = "NewTechBot"
except AttributeError as e:
print(e)
OUTPUT:
RoboX TechBot property 'name' of 'ImmutableRobot' object has no setter property 'brandname' of 'ImmutableRobot' object has no setter
Using the dataclass Decorator
from dataclasses import dataclass
@dataclass(frozen=True)
class