python-course.eu

4. Creating Immutable Classes In Python

By Bernd Klein. Last modified: 19 Feb 2024.

Why do we need immutable classes?

Motionless pantomine as a symbol for 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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See our Python training courses

See our Machine Learning with 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