‘Everything in Python is an Object’

Mutable, Immutable… everything is an object!

Huy Nguyen
Python in Plain English

--

Curiosity cabinet: objects everywhere

Python is an object-oriented programming language, and in Python everything is an object.

Almost every object has some metadata (called attributes) and associated functionality (called methods).

Even these attributes and methods of objects are themselves objects with their own type information.

Every object can be assigned to a variable or passed as an argument to a function.

type() and id()

The type() function returns the type of the object. The returned type is a class as each object is an instance of the corresponding class. Class is a structure used to describe and access a particular value’s information and methods.

From my previous note, Dog.species and sam.age are respectively of type str and int. And sam is of the custom class Dog.

fig1: Type function

The id() built-in returns an unique id for the specified object. Anid() is assigned to every object when it is created.

fig2: Examples of id() in hex format

In the CPython, the reference implementation of Python, the object’s id is an integer memory address that is unique for the given object and remains constant during its lifetime.

Two objects with non-overlapping lifetimes may have the same id() value.

Is is a comparison operator that tests for object identity: a is b is true if and only if a and b are the same object / id. Whereas the == operator compares their values.

fig3: Difference between is and ==

Even though an object Id remains constant during its lifetime, that’s not always true for the object value. That depends on whether the class of an object is mutable or immutable.

fig4: Immutable & mutable classes

Mutable objects can change their values or contents without having their Ids changed, and immutable objects can’t. In other words, the value of immutable objects are tied to their Ids, the value of mutable objects are not.

Warning: CPython has implemented some optimization mechanisms such as small integer caching and string interning so that 1 is 1 or "a" is "a" returns True. These are exceptions that will be further explained at the end of the note. Don’t let it confuse you, it is not related with the objects in question being mutable / immutable.

Mutable objects

The mutable characteristic of an object means that it can be modified and its Id will remain the same.

fig5: Same value, different Ids

Any distinct object stored in a distinct place will have a distinct id.

This is especially important for mutable objects, like lists. If an object can be changed, then you can create two different objects with the same contents.

Like a and b with the same content, have different Ids, and if you change a, b will not change. And since a is a mutable object, when you modify its content, the object Id remains.

fig6: the content’s changed, not the id()

When you create a new list and assign it back to the name a, it will create a new object and therefore a new id will be assigned.

fig7: New Id created

Please note that a = a + [4] and a += [4] are not identical even though they produce the same result (a = [1, 2, 3, 4]). The latter method does not create a new object in memory, it modifies the existing list object, and returns a reference to it.

fig8: No new Id created

When you assign one variable to another, like c = b in fig5, no new object is created, a new tag is bound to the same value.

Because the same list and Id has two different names, b and c, we say that it is aliased. Changes made with one alias will affect the other. In general, it is safer to avoid aliasing when working with mutable objects.

fig9: with the same Id, change in an object affects the other

Immutable objects

Generally speaking, when a new object is created, a new address / Id will be assigned to it. Immutable objects will keep the same ID unless its value is changed.

fig10: Can’t change the content without a new Id

In the example above an attempt to modify the immutable object has landed in an error. When a new value is assigned, a new Id is affected to the object, meaning they have taken two different places in memory.

Tuple & list

Tuples are similar to lists. You use tuples when you don’t want to edit the values once defined. If you want to hold list of items that need to be modified, you would use a list.

Remember when I’ve stated earlier that the value of immutable objects are tied to their Ids? Well, there is a special case when a tuple, an immutable object, holds a reference to any mutable object, such as a list.

fig11: Change the value, not the Id

If you try to change the list within the tuple, you can, and the tuple Id does not change: it’s an immutable object!

From the data model page:

The value of an immutable container object that contains a reference to a mutable object can change when the latter’s value is changed; however the container is still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.

Immutable vs Mutable

Immutables are used when you need to ensure that the object you made will always stay the same.

You don’t need to make copies when returning or passing to other functions, since there is no possibility an immutable object will be modified behind your back.

Operations like map, reduce, filter replace the mutable way of doing things: writing a loop that modifies a collection in place (map), that accumulates some value by mutating it (reduce), or that mutates a collection by removing some of its elements (filter).

Immutable objects are quicker to access than mutable objects.

Mutable objects are great when you need to change the size of the object.

How arguments are passed to functions

If an immutable object is passed to a function, like a in the example here below, this object a retains its identity beyond the scope of the function.

fig12: immutable object
fig13: Immutable object

If you work with an immutable type, like a string, and wish to pass it to a function that appends an exclamation point.

To pick up the appended exclamation point beyond the scope of the function, you’d need to specifically return the alteration.

If a mutable object is passed to function, like a in the example here below, the changes will be visible outside of the scope of the function.

fig14: mutable object

Note that is true as long as the function doesn’t assign a new value to the object, for example n = n + [4] instead of n.append(4), to the object. In which case the binding between the previous Id and the object is broken, and a new object / id is created.

fig15: new object

Exceptions: CPython optimizations

CPython has implemented some optimization mechanisms such as small integer caching and string interning.

fig16: small integer caching

Small integer caching

CPython keeps an array of integer objects for all integers between -5 and 256. So a block of 262 objects integers is initialized at startup.

Every time an integer is created in that range, they simply back reference to the existing object.

The example above shows that a and b have the same object Id and of course the same value. There is no object created in this code.

Within CPython, this occurs with the assignment to the macros NSMALLPOSINTS and NSMALLNEGINTS.

fig17: out of range, no cache mechanism

With an integer out of the -5 and 256 range, you can see that a and b are two distinct objects with the same value. Two objects are created with this code. The small integer caching mechanism does not apply. This is a standard way to deal with immutable objects.

String interning is the method of caching strings in memory as they are instantiated. The idea is that, since strings in Python are immutable objects, only one instance of a particular string is needed at a given time. Any references to that same string can be directed to refer to, instead of taking up new memory.

In the example here below, only one object is created, with the command a = "hello_world".

fig18: string interning mechanism

By the time of this note, I’m running Python 3.7.4, and there is no limitation regarding the number of characters accepted by the string interning mechanism, as long as the string is composed of ASCII letters, digits, or underscores.

With the exception of Empty strings, which are also interned. There is only one object created with the code below.

fig 19: the exception of empty string

That’s all folks !

--

--