Skip to content

Understanding Python Pointers Effortlessly

[

Pointers in Python: What’s the Point?

by Logan Jones intermediate python

If you’ve ever worked with lower level languages like C or C++, then you’ve probably heard of pointers. Pointers allow you to create great efficiency in parts of your code. They also cause confusion for beginners and can lead to various memory management bugs, even for experts. So where are they in Python, and how can you simulate pointers in Python?

Why Doesn’t Python Have Pointers?

The truth is that I don’t know. Could pointers in Python exist natively? Probably, but pointers seem to go against the Zen of Python. Pointers encourage implicit changes rather than explicit. Often, they are complex instead of simple, especially for beginners. Even worse, they beg for ways to shoot yourself in the foot, or do something really dangerous like read from a section of memory you were not supposed to.

Python tends to try to abstract away implementation details like memory addresses from its users. Python often focuses on usability instead of speed. As a result, pointers in Python don’t really make sense. Not to fear though, Python does, by default, give you some of the benefits of using pointers.

Understanding pointers in Python requires a short detour into Python’s implementation details. Specifically, you’ll need to understand:

  1. Immutable vs mutable objects
  2. Python variables/names

Immutable vs Mutable Objects

In Python, everything is an object. For proof, you can open up a REPL and explore using isinstance():

>>> isinstance(1, object)
True
>>> isinstance(list(), object)
True
>>> isinstance(True, object)
True
>>> def foo():
... pass
...
>>> isinstance(foo, object)
True

This code shows you that everything in Python, including basic data types like integers (int), lists (list), and boolean values (bool), is an object. An object in Python is a piece of memory that holds data and has associated behavior and methods. Each object has a unique memory address to identify it.

Now let’s explore the concept of mutability. In Python, an object can be either immutable or mutable. Immutable objects cannot be changed after they are created, while mutable objects can be modified. Here are a few examples:

  • Immutable objects: integers, floating-point numbers, strings, tuples
  • Mutable objects: lists, dictionaries, sets

Let’s see how this works with some examples:

>>> a = 5
>>> b = a # 'b' points to the same memory location as 'a'
>>> a = 10
>>> a
10
>>> b
5

In this example, a and b are both variables that hold the value 5. When we update a to 10, a now points to a new memory location with the value 10, while b still points to the original memory location with the value 5.

>>> x = [1, 2, 3]
>>> y = x # 'x' and 'y' point to the same memory location
>>> x.append(4)
>>> x
[1, 2, 3, 4]
>>> y
[1, 2, 3, 4]

In this example, x and y are both variables that hold a list. When we modify x by appending a new element to it, both x and y reflect the change because they point to the same memory location.

Understanding Variables

Now that you have a basic understanding of objects and mutability in Python, let’s dive into how variables and names work in Python.

Variables in C

In C, you can think of variables like named memory addresses. You define a variable by specifying its type and a name, and the compiler reserves a chunk of memory to store the value of that variable. For example:

int x = 5;
int y = x;

In this C code, x and y are both variables that hold an integer value. x is assigned the value 5, and y is assigned the value of x. In memory, x and y are located at different memory addresses, and x holds the value 5, while y holds the same value as x.

Names in Python

In Python, variables are not like named memory addresses. Instead, variables are just names or labels that are bound to objects. When you assign a value to a variable, you are binding that name to the object in memory. For example:

x = 5
y = x

In this Python code, x and y are both names that refer to the same object in memory with the value 5. They are not separate memory locations like in C. When you update the value of x, you are actually creating a new object in memory and rebinding the name x to that new object. The name y still refers to the original object with the value 5.

A Note on Intern Objects in Python

In some cases, Python automatically interns objects in memory for performance reasons. Interning is the process of reusing objects with the same value. For example, in Python, small integers from -5 to 256 are interned. This means that the same object is reused when you create variables with these values. Here’s an example:

a = 5
b = 5
a is b # Returns True

In this example, the objects a and b both refer to the same memory location because the value 5 is interned. However, this behavior may vary across different Python implementations or versions, so it’s not something you should rely on.

Simulating Pointers in Python

Now that you have a better understanding of objects, mutability, and variables in Python, let’s explore how you can simulate pointers in Python.

Using Mutable Types as Pointers

One way to simulate pointers in Python is by using mutable types as pointers. Since mutable objects can be modified, you can pass them around and update their values. Let’s see an example:

def update_value(pointer):
pointer[0] = 10
lst = [5]
print(lst) # [5]
update_value(lst)
print(lst) # [10]

In this example, the function update_value() takes a list as a parameter and updates its first element to 10. When you pass the list lst to the function, you are effectively passing