Immutability in Python

Famidha Thurab
Make Android
Published in
6 min readFeb 1, 2024

This article covers how immutability plays a vital role in the utilization of system RAM with Python.

Photo by Justin Wei on Unsplash

In the bustling world of Python, where code dances dynamically, variables weave their tales through mutable landscapes, there lies a hidden gem — an elegant concept often overlooked but with the power to transform the way we perceive and craft our code: immutability.

Imagine a journey through the enigmatic corridors of a programming paradigm where values, once set, remain eternally unchanged, and each line of code tells a story of resilience and predictability.

Join me as we unravel the mystique of immutability in Python, a journey that promises the narrative of code that stands the test of time.

Welcome to the realm of immutable elegance in Python!

What is Immutability?

Immutability in programming languages refers to the property of objects or data structures that prevents them from being modified after creation. Once an immutable object is created, its state cannot be changed. This can simplify code, enhance predictability, and aid in debugging by avoiding unexpected side effects.

Immutability in Python

Immutability in Python is a foundational concept that influences the design of certain data types. In Python, objects of immutable types cannot be altered after creation, providing a level of predictability and safety in programming. Key examples include strings, tuples, integers, floats, and Boolean. Understanding immutability is crucial for effective Python development, as it shapes how data is manipulated and shared within the language. This characteristic encourages the creation of new objects instead of modifying existing ones, contributing to code clarity and reducing unexpected side effects.

Immutable and non-immutable data types in Python!!

So far we have studied the concept of Immutability as a technical prospect now is the time to explore the mystery of Oh!! Really!!

First of all!! we need to do a memory-based analysis so you can use the Id() function to get the memory address of any variable.

Example:

interger_var = 10
print("Address interger_var:", id(interger_var))
string_var = "String"
print("Address string_var:", id( string_var))
boolean_var = True
print("Address boolean_var:", id(boolean_var))
list_data = [1, 2, 3]
print("Address list_data:", id(list_data))
touple_data = (1, 2, 3)
print("Address touple_data:", id(touple_data))

Output:

Address interger_var: 140355518706024
Address string_var: 140355506017136
Address boolean_var: 140355518704832
Address list_data: 140355509841856
Address touple_data: 140355507710208

Why Python included all primitives including a data structure Tuples as an immutable type?

Making a data type immutable in programming ensures stability, predictability, and safety. Immutability eliminates unintended changes, simplifies code understanding, supports concurrency, enables hashability, aligns with functional programming principles, and may lead to optimization benefits. It’s a design choice that enhances reliability but may not be suitable for scenarios requiring frequent modifications.

Does immutability improve performance?

This is specific on a case basis! while immutability can provide performance benefits in specific situations, it’s crucial to assess the requirements of your application and choose the appropriate data type accordingly.

Immutability is a design choice that depends on the use case. For scenarios where data rarely changes and thread safety is crucial, the benefits of immutability might outweigh any potential overhead.

In situations where mutability is necessary, and performance is a primary concern, mutable data structures might be preferred.

Why Tuples is immutable?

This we can easily understand by differentiating the Tuples with List. The Difference between lists and tuples in Python is their mutability context.

Lists are mutable, meaning you can modify their elements after the list is created. You can add, remove, or change items in a list. Tuples are immutable, and once created, their elements cannot be modified. You cannot add, remove, or change elements in a tuple.

Listpyth
my_list = [1, 2, 3]
Tuple
my_tuple = (1, 2, 3)

The use case list and Python specifically can be used!

We can use the list when a collection of elements can be modified, such as adding or removing items dynamically. Lists are suitable for situations where you need a mutable sequence of elements.

We can use tuples when an immutable sequence of elements is needed. Tuples are often used to represent fixed collections of items, like coordinates or configurations, where the order and types of elements matter.

Is it related to Performance?

Due to mutability, lists might have a slightly higher memory overhead and may be less performant in certain scenarios compared to tuples where a Tuple’s immutable nature allows for potential optimizations, making tuples slightly more memory-efficient and faster in certain operations.

How does String Concatenation work in a loop?

Finally, we are discussing the most interesting topic of this article, I will write a sequence of code and its outputs which will make it more clear other hen writing it into words.

I created the following strings with different Strings:

string_var = "A"
print(id( string_var))
string_var = "B"
print(id( string_var))
string_var = "C"
print(id( string_var))
string_var = "D"
print(id( string_var))
string_var = "E"
print(id( string_var))
string_var = "F"
print(id( string_var))

Output:

140382002392352
140382001980128
140382001980256
140382002392520
140382002392576
140382002392632

Due to Immutability, all the strings are allocated to different memory allocations.

One more similar case:

string_var = "A"
print(id( string_var))
string_var = "A" + "B"
print(id( string_var))
string_var = "A" + "B" + "C"
print(id( string_var))
string_var = "A" + "B" + "C"

Output:

140246077730080
140246068819504
140246077387392

Now we will see how Python is intelligent enough to detect what is happening in the code:

string_var = "A"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "B"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "C"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "D"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "E"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "F"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "G"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "H"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "I"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "J"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "K"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "L"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "M"
print("string_var: ", string_var, id( string_var))
string_var = string_var + "N"
print("string_var: ", string_var, id( string_var))

Output:

string_var: A 140270208281888
string_var: AB 140270197136432
string_var: ABC 140270195762288
string_var: ABCD 140270197136432
string_var: ABCDE 140270195762288
string_var: ABCDEF 140270197136432
string_var: ABCDEFG 140270195762288
string_var: ABCDEFGH 140270197136432
string_var: ABCDEFGHI 140270195762288
string_var: ABCDEFGHIJ 140270197136432
string_var: ABCDEFGHIJK 140270195762288
string_var: ABCDEFGHIJKL 140270197136432
string_var: ABCDEFGHIJKLM 140270195762288
string_var: ABCDEFGHIJKLMN 140270197136432

Can you see the reusing of the addresses 140270197136432 and 140270195762288?

So this is how even a one-step-ahead optimization on String when the same string is concatenated again and again. Which is certainly will not happen with the below code:

string_var = "A"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B" + "C"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B" + "C" + "D"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B" + "C" + "D" + "E"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B" + "C" + "D" + "E" + "F"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B" + "C" + "D" + "E" + "F" + "G"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B" + "C" + "D" + "E" + "F" + "G" + "H"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B" + "C" + "D" + "E" + "F" + "G" + "H" + "I"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B" + "C" + "D" + "E" + "F" + "G" + "H" + "I" + "J"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B" + "C" + "D" + "E" + "F" + "G" + "H" + "I" + "J" + "K"
print("string_var: ", string_var, id( string_var))
string_var = "A" + "B" + "C" + "D" + "E" + "F" + "G" + "H" + "I" + "J" + "K" + "L"
print("string_var: ", string_var, id( string_var))

Output:

string_var:  A 139743095479584
string_var: AB 139743082964080
string_var: ABC 139743095136896
string_var: ABCD 139743082964400
string_var: ABCDE 139743082964656
string_var: ABCDEF 139743082964976
string_var: ABCDEFG 139743082965360
string_var: ABCDEFGH 139743082965808
string_var: ABCDEFGHI 139743082966320
string_var: ABCDEFGHIJ 139743082966896
string_var: ABCDEFGHIJK 139743082967536
string_var: ABCDEFGHIJKL 139743082968240

I hope this article is helpful. Please comment for any discussion in the comment box.

--

--

Famidha Thurab
Make Android

DevOps Engineer| Editor Make Android | Writes on AIOps | Android IVI | AOSP | Docker | Kubernetes | AWS | Jenkins | Python | Shell | Git