Python3: Mutable, Immutable… everything is object!

Marcelo Arbiza
5 min readOct 7, 2021

--

Like everything in Python it is an object. Every time we create an object, it will be assigned a unique ID (address of the object in memory) and it will not change once there is been created.

Each object has an id, a type, and a value.

  • Id: It never changes and uniquely identifies the object. The is operator lets us know if two objects are actually the same. That is, if two variables refer to it.
  • Type: It indicates the type to which it belongs, such as a float or a list. The type () function tells us the type of a certain object. It is the class to which it belongs.
  • Value: Every object has particular characteristics. If these characteristics can be modified, we will say that it is a mutable type. Otherwise, it is immutable.

The different types of objects in general, can be classified according to their mutability. They may be:

Mutables: If they allow to be modified once created.
Immutable: If they do not allow to be modified once created.

The following types are mutable:

Lists
Bytearray
Memoryview
Dictionaries
Sets
And user defined classes

And they are immutable:

Booleans
Complexes
Integers
Float
Frozenset
Chains
Tuples
Range
Bytes

I will show you the difference with an example.

List (mutable): Can be modified

Tuple (inmutable): Can´t be modified.

You could modify the tuple by converting it to a list, modify it, and then convert it to a tuple again, but this would modify its id.

Integers(inmutable):

Same value, same id.

List (mutable):

Although it has been modified, its id is the same since the lists are mutable.

And why is this important?

The main differences between mutable and immutable types are as follows:

Immutable types are generally faster to access. So if you don’t plan to modify a list, it is better that you use a tuple.
Mutable types are perfect when you want to change their content repeatedly.Immutable types are expensive to change, since what you actually do is make a copy of their content into a new object with the modifications.

The mutability of objects is a very important feature when dealing with functions, since Python will treat them differently.

Immutable types are passed by value, therefore within the function a copy is accessed and not the original value.
Mutable types are passed by reference, as is the case with lists and dictionaries. Something similar to how C passes arrays as pointers.

Integer pre-allocation

Unlike C, Python integers are objects that use an internal data structure for storage. It is completely different from basic arithmetic types like integers or real numbers. This is what makes a Python integer size different from other languages.

Whenever Python encounters an assignment like x = 3, it tries to construct an integer object. In Python, an integer object is initialized using the following data structure.

typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;

The PyObject_HEAD is macro which expands to a reference counter of type Py_ssize_t and a pointer of PyTypeObject type.

Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;

Next, Python reserves a pre-allocated block of Integer objects. And use it to serve new integer request instead of making allocations on every assignment. Here is the block structure used to hold integer objects.

struct _intblock {
struct _intblock *next;
PyIntObject objects[N_INTOBJECTS];
};
typedef struct _intblock PyIntBlock;

Once a block’s integer object group gets spent, a new block becomes available to fulfill the new requests for integer objects. All these blocks are chained together in the form of a single linked list. This structure is basically used to access these integers fast.

Python handles small integers in a little different fashion than big integers. It uses a PyIntObject type array of 262 pointers to store the small integers falling in the range from -5 to 256.

static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

It means that the small integers will not use the group of objects. Instead, they will use the list of pointers given above. Because, the small integers will end up using the same PyIntObject integer object but with a different reference count.

On the contrary, the big integers get their allocation from the integer object pool. And each of them would have its own PyIntObject type object.

Tuples vs frozensets in Python

Tuples:

  • Immutable lists
  • Are indeed an ordered collection of objects, but they can contain duplicates and unhashable objects, and have slice functionality

Frozensets:

  • Immutable sets
  • Aren’t indexed, but you have the functionality of sets - O(1) element lookups, and functionality such as unions and intersections. They also can't contain duplicates, like their mutable counterparts.

--

--