Variables refer to addresses

Here is (almost) the real story of how data is handled in Python. Variables refer to addresses (cells) in memory, where the actual data is stored. For example, let's definte the variable n and initialize it to 42:

In [1]:
n = 42

We can get the address (memory cell) to which n refers using

In [2]:
id(n)
Out[2]:
9996800

This means the integer 42 is stored at address 9996800. When we go print(n), what we do is look up variable n, see that it refers to address id(n), and print whatever is found at address id(n).

Let's now assign n to another variable, and see what address that variable refers to.

In [3]:
m = n
id(m)
Out[3]:
9996800

This makes sense -- m and n both should evaluate to 42, so there refer to the same address, which contains 42.

So far, so boring -- we just introduced one more level of indirection. Here is where things become interesting. Let's define a list.

In [4]:
L = [1, 2, 3]
id(L)
Out[4]:
139922587453128

Let's now assign L to another variable:

In [5]:
L1 = L
id(L1)
Out[5]:
139922587453128

What's important to see here is that L and L1 refer to the same address. In other words, there is just one list, and both L and L1 refer to it. That means that if we modify the contents of the list (e.g., by changing the first element), both L and L1 will be affected (since they are the same list!)

In [6]:
L[0] = 4
print(L, L1)
[4, 2, 3] [4, 2, 3]

Both L and L1 were affected by L[0] = 4!

When two variables refer to the same address, the variables are said to be aliases of each other. The phenomenon itself is known as aliasing.

Note that m and n above are also aliases. It just wasn't very important, since we cannot change the contents of an integer in the same way that we can change the contents of a list.

Mutability and immutability

Objects whose contents you can change (e.g., lists) are said to be mutable. Objects whose contents you cannot change (e.g., ints and floats) are said to be immutable.