Python’s Mutable and Immutable Objects

Introduction

Python is currently one of the most popular languages used today in software development. In the 2016 Github Octoverse, Python ranks third in all programming languages on Github’s Octoverse (https://octoverse.github.com).

The language is easy-to-learn and use. But, let’s start with the basics. You will soon find out that everything in Python is an object. A variable is an object, a string is an object, a integer is an object, even math comparator symbols are objects (<, >, =, etc signs) which you can redefine (or overload).

One of the unique aspects of Python is that variables can be stored in a couple of ways: stored separately in different memory addresses (Panel A) or aliased (shared with another variable) as seen in the image below (Panel B).

Panel A: Objects stored separately. Panel B: Objects that are aliased

Since this can cause obvious confusion, we need a way to differentiate between the two different types of objects. How? Let’s use the Python REPL by typing python3 in a terminal window, then check whether the VALUES of a and b match, you can use the == comparator.

>>> a == b
True

To test whether two variables refer to the same object use the is operator.

>>> a is b
False

Or, another simple solution is to use id() and type() functions.

id() and type()

Example of id() and type()

To find the memory address of a variable, use id([VarName]). In the image above, you see that both variables hold the value of 1 in the memory address 10055552 and that both a and b are integers. Both variables are actually labels that point to a specific integer, 1, in an array of integers that python keeps in memory. The values from -5 to 256 are pre-allocated (stored) in an array in order to save processing time. These range of integers are arbitrarily determined to be the more commonly-used integers. You can think of these variables as labels to the integers and each of these special integers have a special counter to know how many variables use that value.

Numbers outside the pre-allocated range are newly-allocated and in a different part of memory.

type() is a built-in command that tells you what class the variable or value is. Some of the types that are in Python include Mutable objects (list, dict, set, bytearray, and custom classes/objects) and Immutable objects (int, float, str, tuple, complex, bytes, frozenset).

Mutable objects

Python has designated certain data types to be mutable. Mutable variables can change its value and can change their values in-place (without making a copy of itself first). These include list, set, dict, bytearray, and user-definedcustom classes/objects. These type of variables support assignment operations and in-place assignments.

A Python list type has in-place assignment and attribute assignment

Immutable objects

The other group of objects include immutable types, such as int, float, str, tuple, complex, bytes, and frozenset. At times it is useful to protect the values of data or data structures, and immutable types serve this need. Attempts to change its value directly will generate an error. However, the variable can be completely reassigned.

A Python tuple type DOES NOT have in-place assignment but the variable can be reassigned

It is also interesting to note if that a string is treated differently by the Python Memory Manager if it has white space in it. If it does, then it’s allocated separately. If the string has no white-space, other variables that have the same value will point to the same memory location as the original variable.

A Python str (string) is optimized in memory if there is no white space

Also, strangely enough, an immutable tuple cannot be changed. However, if the element(s) inside the tuple are mutable, then the element(s) can be changed.

Why is Mutability and Immutability Important?

When programming, it’s important to keep in mind how the computer is processing your code. If memory is limited or processing speed is important, say, in BigData applications, then you will want to optimize your code. For example,

String Concatenation

But, it can be expensive to create because strings are immutable and concatenating strings must allocate memory for a new string and copy the contents. In the above code, the resulting string is created from separate strings. It may be more Pythonic to use one of the methods below because it creates the final string with less objects by appending. Appending to a list (in most cases) requires no allocation. Immutable objects are fundamentally expensive to change, but mutable objects are cheap.

3 alternate ways for String Concatenation

How arguments are passed to functions and what does that imply for mutable and immutable objects

Python is neither strictly “call-by-reference” nor “call-by-value”. In Python a variable is not an alias for a location in memory. Rather, it is simply a binding to a Python object.

From Learning Python by David Ascher, Mark Lutz:

Immutable arguments act like C’s “by value” mode

Objects such as integers and strings are passed by object reference (assignment), but since you can’t change immutable objects in place anyhow, the effect is much like making a copy.

Mutable arguments act like C’s “by pointer” mode

Objects such as lists and dictionaries are passed by object reference too, which is similar to the way C passes arrays as pointers — mutable objects can be changed in place in the function, much like C arrays.

So, go ahead and try Python yourself, which can be easily done by playing with Python’s REPL (interactive interpreter). See how Python manages its objects. It’s a powerful language, and it has its idiosyncrasies.