Variables and memory addresses in Python

Daniel Tooke
3 min readAug 2, 2019

--

Without variables, you’ll only get lost in your computer’s memory

One thing I’ve found really helpful as I’ve got to know Python better is to understand the way it assigns objects to memory addresses. This is ultimately pretty simple, but in my opinion it’s the best way to understand variable assignment and the equality operators.

One of the earliest things you’re often told about Python is that everything is an object. Everything. Each and every string, number, list, dictionary, class, function, the boolean objects True and False, and the None object. Python will store each of them at a particular memory address, and you can find out where in memory they’re stored using the id() function:

>>> id(10)
4304841504
>>> id(str)
4304548256
>>> id(True)
4304449920
  • Note: This assumes you’re using version 3 or above of standard CPython

When you assign a variable, you might think that each variable is a new object, but that’s not really true:

>>> a = [1, 2, 3]
>>> b = a
>>> b.append(4)
>>> print(a)
[1, 2, 3, 4]

When I first started out in Python I once thought I now had two separate lists, a and b, and could make any changes to them I wanted independent of the other. But no, as seen here, any changes made to b in the above example also impact a and vice-versa.

I find the easiest way to explain this is to remember that when you assign a variable, you’re really pointing it to a memory address. So when I did a = [1, 2, 3] I created a new list object in memory, and then saved a as a variable that points to the memory address of that object. And what I’d done in the above example was point two variables, a and b, to the same memory address:

>>> id(a)
4522039944
>>> id(b)
4522039944

When you use a or b in your subsequent Python code, what the program is really doing is going off and retrieving the object it’s pointing to at memory address 4522039944. If you subsequently reassign b to some other object, e.g. b = True, it now points to a different memory address instead, where there’s a different object stored.

Incidentally, if I really did want to create two different list objects like in my example above, the way to do this is using Python’s copy function:

>>> import copy
>>> c = copy.copy(a)
>>> id(a)
4522039944
>>> id(c)
4522037832

Now we have two separate list objects, at two separate memory addresses, and can add/delete items to them independently.

Finally, another reason I feel that understanding memory addresses is helpful is because it’s the clearest way to understand the equality operators, particularly is.

In short, the double equals == can be used to compare the two objects and see if they’re equal to one another (with two lists, that would necessitate them having the same contents). Conversely, the is operator will test whether they are the same object, which for our purposes means they are at the same memory address:

# a and b have the same contents, and are the same object>>> a == b
True
>>> a is b
True
# a and c have the same contents, but are two different objects at different memory addresses>>> a == c
True
>>> a is c
False
# If we add another object to c, it's now got different contents to a, as well as being a different object at a different memory address>>> c.append(5)
>>> print(c)
[1, 2, 3, 4, 5]
>>> print(a)
[1, 2, 3, 4]
>>> a == c
False
>>> a is c
False

As I say- this is simple once you get it, but I’ve found it helpful as a foundational principle, thinking not just of objects as metaphorical ‘things’ that exist in the Python universe, but grounded in a particular place in memory, and being accessed because variables point to that particular memory address. There are a few other interesting tidbits that come out of this understanding, such as interning and garbage collection, and I’ll cover those in my next few posts.

--

--