Mutable vs. immutable objects¶

Two examples¶

Objects used in Python can be separated into two major categories: some objects are mutable while other objects are immutable. The difference between these categories is illustrated by the following two examples.

Example 1. In the code below we assign a variable m to the integer 2, and set n equal to m. In effect both of these variables have value 2:

m = 2
n = m
print('m={}'.format(m))
print('n={}'.format(n))


m=2 n=2

When we change the value of n the value of m is not affected, it is still 2:

n += 1
print('m={}'.format(m))
print('n={}'.format(n))


m=2 n=3

Example 2. Here we perform the same operations as in above, but we assign to the first variable a list and then make the second variable equal to the first one. Again the result is that these variables have the same value:

m = [1, 2, 3]
n = m
print('m = {}'.format(m))
print('n = {}'.format(n))


m = [1, 2, 3] n = [1, 2, 3]

Next, we modify the value of n by changing the first element of the list:

n[0] = 100
print('m = {}'.format(m))
print('n = {}'.format(n))


m = [100, 2, 3] n = [100, 2, 3]

The value of n changed as expected, but value of m changed as well even though we did not use this variable.

The difference in the behavior of variables in Example 1 and Example 2 comes from the fact that integers are immutable objects while lists are mutable.

How this works¶

When a variable is a assigned to some object (a number, string, list etc.) the variable does not store this object, but rather a computer memory address where the object resides. We can use the id() function to check this memory address:

m = 2
print('id(m) = {}'.format(id(m)))


id(m) = 400004

When we execute n=m the variable n stores the same address as m, and in effect both variables point to the same object:

n = m
print('id(n) = {}'.format(id(n)))


id(n) = 400004

Immutable objects (e.g. integers) are objects that cannot be modified without copying them to a new memory location. Thus, if we change the value of n its memory address will also change:

n += 1
print('n = {}'.format(n))
print('id(n) = {}'.format(id(n)))


n = 3 id(n) = 400002

Meanwhile, the address stored by the variable m is unchanged, so this variable points to the same object as before:

print('m = {}'.format(m))
print('id(m) = {}'.format(id(m)))


m = 2 id(m) = 400004

Next, we perform the same operations using lists instead of integers:

m = [1,2,3]
n = m
print('id(m) = {}'.format(id(m)))
print('id(n) = {}'.format(id(n)))


id(m) = 400004 id(n) = 400004

As before we get two variables that store the same memory location, and so point to the same object.

The difference is that a list is a mutable object, which means that it can be modified “in place” i.e. without changing its memory location. If we change one element of the list n then the list will change, but its memory location will not:

n[0] = 100
print('n = {}'.format(n))
print('id(n) = {}'.format(id(n)))


n = [100, 2, 3] id(n) = 400004

Since the address stored by the variable m is still the same as the address stored by n, the value of m will be equal to the new value of n:

print('m = {}'.format(m))
print('id(m) = {}'.format(id(m)))


m = [100, 2, 3] id(m) = 400004

Why it works this way¶

Lists are often very large objects, consisting of thousands or millions of elements. If lists were immutable changing even a single element of a list would require copying the whole list which could slow a program a lot. Since lists are mutable this is avoided: changing an element of a list is done without touching any other elements.

On the other hand, integers usually take very little space in computer memory and can be copied to new memory locations quickly. Having immutable integers does not affect program efficiency in a substantial way, and it assures that by changing value of one integer variable we will not inadvertently change some other variable.

1) Some examples of immutable objects are:

• integers

• floats

• booleans

• strings

• tuples

Mutable objects include:

• lists

• dictionaries

• sets

• numpy arrays

2) Lists (as well as other mutable objects) have a copy() method that creates a copy of a list that can be modified independently of the original:

m = [1, 2, 3]
n = m.copy() #makes a copy of the list
n[0] = 100
print('n = {}'.format(n))  # n was modified
print('m = {}'.format(m))  # but m is unchanged


n = [100, 2, 3] m = [1, 2, 3]

3) The is operator checks if two variables point to the same memory location:

m = 'hello world!'
n = m
m is n


True

The meaning of is is different that the meaning of ==. The equality operator == checks is two variables have the same value, which may be the case even if these variables point to different locations in the computer memory:

m = 'hello world!'
n = 'hello world!'

m == n # checks if m, n have the same value


True

m is n # checks if m, n point to the same  memory locations


False