Mutability and Containers

8 Dec 2019

Containers

A container is an object that contains a reference to another object. A common example would be a List.

>>> shopping_list = ["milk", "bread", "eggs"]
>>> onions = "onions"
>>> shopping_list.append(onions)
>>> shopping_list
['milk', 'bread', 'eggs', 'onions']
>>> id(onions)
4563363056
>>> id(shopping_list[3])
4563363056

In this example, we are creating a List called shopping_list and added the onions object to it. shopping_list contains a reference to the onions variable. We can easily see that by checking the identity of onions and the element in the List we have just added.

Mutability

An objects mutability tells us whether or not we can change its value. If it is mutable, we can change it. If it is immutable, we cannot. An object’s mutability comes from its type.

It is important not to get an objects mutability confused with the mutability of a variable. For example:

>>> number = 5
>>> id(number)
4362428304
>>> number += 1
>>> number
6
>>> id(number)
4362428336

In this example, we have the object 5 assigned to the variable number. We then increment number by 1. Number objects in Python are immutable (well, you can’t change the value of 5 to something else can you?) so what we see here is that the result of the expression is a new object (in this case the object for the number 6). This is then assigned to the number variable. We can see this in action by looking at the identity of the object behind the number variable before and after the expression.

Other common immutable types in Python are strings, bools and tuples.

Mutable Containers

You might be asking what types are mutable. There are two common mutable types. Dictionaries and Lists.

A List is mutable because you can add and remove items. E.g.

>>> shopping_list = []
>>> id(shopping_list)
4456599792
>>> shopping_list.append("milk")
>>> id(shopping_list)
4456599792

In this example, we create the List shopping_list and add “milk” to it. The identity behind the shopping_list variable doesn’t change, so we know that it is the same object. However, we have changed its value (by adding an element to the List) so we know it must be mutable.

Immutable Containers

The official documentation on the Python Data Model says:

In most cases, when we talk about the value of a container, we imply the values, not the identities of the contained objects; however, when we talk about the mutability of a container, only the identities of the immediately contained objects are implied. So, if an immutable container (like a tuple) contains a reference to a mutable object, its value changes if that mutable object is changed.

What does this mean? Well, it means several things:

1. The value of a container is typically the combination of the value of the items it contains.

>>> shopping_list = ["milk"]
>>> shopping_list
['milk']
>>> shopping_list.append("bread")
>>> shopping_list
['milk', 'bread']

When we add “bread” to the shopping list, the List’s value changes.

2. A container is immutable if the collection of things it holds cannot change.

>>> shopping_list = ["milk"]
>>> shopping_list.append("bread")
>>> shopping_list
['milk', 'bread']
>>> final_shopping_list = tuple(shopping_list)
>>> final_shopping_list.append("eggs")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'append'

A tuple is immutable, you cannot add additional items to it.

3. Normally, an immutable object’s value cannot change however, when it is a container that holds mutable objects, its value can change.

>>> my_tuple = tuple([5, "hello", ["one"]])
>>> my_tuple
(5, 'hello', ['one'])
>>> my_tuple[0] = 6
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> my_tuple[2].append("two")
>>> my_tuple
(5, 'hello', ['one', 'two'])

A tuple that contains a number, cannot change that number. This is because the number object is immutable, so to have a different number would mean having a different number. Because a tuple is an immutable container, the identities it contains cannot change. So the number cannot change.

However, a List is a mutable type. We can append to the List and change its value, and therefore the tuple’s value. The List is the same object as before, with the same identity, so this is possible.