Mutable and Immutable objects in Python
Introduction
There is a common expression about Python, and it is: “Almost everything in Python is considered as an object.” This expression is almost self-explanatory, but to have a better idea about its meaning, we must define what an object is. An object is a collection of data (variables) and methods that operate on that data. Also, every object has a value, a type, and an ID. During this reading we are going to show the importance of type and variable identifier (id), the differences between mutable and immutable objects, and how Python treats these objects and how the arguments are passed to functions.
Function Type and function Id
When you are using Python and you want to know the type of the object you are interactive with, you can use the function type. Here are some examples of its implementation:
Also, it is important to know the use of the id() function. This function returns the identity (unique integer) of an object, which in other words, is the location of the memory address of the object.
Mutable objects
As we explain before, once an object is created, it has a value, a type, and an ID. The ID of an object never changes. The type also never changes, and the value can either change or not. If the value of an object changes, it is safe to say that the object is mutable. Otherwise, the object is immutable.
Below is an example of a mutable object:
As you can see in the above example, even though one of the values inside the list was changed, the id number did not change. Therefore, after confirming that the object´s type and ID did not change, but the value was successfully changed, we can conclude that the object was a mutable object. Mutable sequences can be changed after creation. Some of Python’s mutable data types are: lists, byte arrays, sets, and dictionaries.
Immutable objects
An immutable object is the one that cannot be change once its created. Its ID, type and value cannot be modified. When the user wants to change the value, instead of making this change, a new object it´s created. This new object has its own ID, type, and value. Below is an example of an immutable object:
As you can see, when the user wanted to change the value of the variable round, a new variable was created. Some immutable types include numeric data types (integers, booleans, floats, complex numbers, fractions, and decimals), strings, bytes, frozen sets, and tuples.
Importance of mutable and immutable objects
Immutable objects are thread safe. The state of an object cannot be changed after its construction. This implies that read-only data is shared among threads, which provides thread safety. Operations involving mutations like (changing value at a particular index) can be implemented in such a way that they create new objects instead of modifying the existing ones. Immutable objects improve correctness and clarity. It provides a sane way to reason about the code. As the value of the object won’t change throughout the execution of the program. We can freely pass around objects without the fear of modification.
Mutable objects are much harder to reason about. Mutable objects with multiple references pointing to the same object also called as aliasing, leads to inconsistencies and threatens correctness of the code. One part of code mutating the value and another part of code affected due to this mutation. This is why it results in inconsistency. In other words, the second name given to a piece of data is known as an alias. Aliasing happens when the value of one variable is assigned to another variable because variables are just names that store references to actual value.
Python has two special scenarios in which it deals with mutable in particular ways. For example, even though a string is immutable, it can be concatenated. Here is an example:
As you can see, a and b (which are strings) can be concatenated to create a string. One disadvantage of this approximation is the fact that the last string “a” is a different object from the original one. Therefore, more memory is required.
Another interesting scenario is the combination of mutable and immutable objects. As we know, tuples are immutable, and lists are mutable. Here is an example of how Python handles a mutable object (list) inside an immutable object (tuple).
As you can see, Python could easily change one of the values inside the list, which, at the same time, is inside the tuple. After this we can conclude that, even though the values inside immutable objects cannot be changed, if the components of those objects are mutable, those values inside mutable objects can be changed.
NSMALLPOSINTS & NSMALLNEGINTS
NSMALLNEGINTS
is in the range -5 to 0 and NSMALLPOSINTS
is in the 0 to 256 range. These are macros defined in Python — earlier versions ranged from -1 to 99, then -5 to 99 and finally -5 to 256. Python keeps an array of integer objects for “all integers between -5 and 256”. When creating an int in that range, it is actually just getting a reference to the existing object in memory.
If x = 42, what happens actually is Python performing a search in the integer block for the value in the range -5 to 256. Once x falls out of the scope of this range, it will be garbage collected (destroyed) and be an entirely different object. The process of creating a new integer object and then destroying it immediately creates a lot of useless calculation cycles, so Python preallocated a range of commonly used integers.
There are exception to immutable objects as stated above by making a tuple “mutable”. As it is known a new object is created each time a variable makes a reference to it, it does happen slightly differently for a few things –
- Strings without whitespaces and less than 20 characters
- Integers between -5 to 256 (including both as explained above)
- Empty immutable objects (tuples)
These objects are always reused or interned. This is due memory optimization in Python implementation. The rationale behind doing this is as follows:
- Since programmers use these objects frequently, interning existing objects saves memory.
- Since immutable objects like tuples and strings cannot be modified, there is no risk in interning the same object.
Frozen sets
The frozenset() is an inbuilt function in Python which takes an iterable object as input and makes them immutable. Simply it freezes the iterable objects and makes them unchangeable.
In Python, frozenset is the same as set except the frozensets are immutable which means that elements from the frozenset cannot be added or removed once created. This function takes input as any iterable object and converts them into an immutable object. The order of elements is not guaranteed to be preserved. Since frozenset object are immutable they are mainly used as key in dictionary or elements of other sets.
Below you can see a couple of examples of the frozenset() function.
How arguments are passed to functions
When you pass arguments to a function, if an object is immutable, it cannot be changed. However, if it is mutable, the original object can be changed.
In this example, we cannot change y and the value always be 3. Although x has the same memory reference as y, when x=x+1, it points to a new memory address. Here is a way to change the value of y.
On the other hand, if you have a mutable object, it can be changed without reassignment. In other words, the original object can be changed. A mutable object, like a list in the following example, can be changed easily without the need of creating another list.
Sources
(Don´t go out without checking these amazing sources)
https://towardsdatascience.com/immutable-vs-mutable-data-types-in