Variable and memory references in Python

Welcome to the first article in my new series, where I'll explain some important ideas about Python that many beginners often miss.

At first glance, it might appear that defining a variable is a straightforward process – you assign a value, and it's ready for use. However, beneath this apparent simplicity lies a deeper mechanism. When we declare a variable, it's not merely a container for data; it's a reference to a specific location in the computer's memory where the actual data resides.

some_var = 100

To illustrate this, consider memory addresses, which are often represented in hexadecimal notation. In our code snippet, we've created a variable named some_var and assigned it the value 100. But here's what's happening behind the scenes: Python is creating an object with a datatype integer and value 100 which is securely stored somewhere in the computer's memory, and some_var now points to the starting memory address of that storage location.

Memory isn't a monolithic entity; rather, it's divided into discrete blocks, each with a finite amount of space. Depending on the size of the object we're dealing with, it may occupy multiple memory blocks.

Whenever we need to use the value stored in a variable, the computer goes back to that reserved memory location to fetch it. If you ever wonder where exactly the object is stored in memory, Python provides handy functions for that purpose.

You can use the id() function, which will return a numerical value representing the memory address of the data. If you prefer a hexadecimal representation, you can use the hex() function. It's important to note that for two different variables, these functions will always return different values because they point to distinct memory locations (except in some cases which we will evaluate more in another article).

some_var = 100
another_var = 200

# Output: 4327092512
# Output: 0x101ea3120

# Output: 4327095712
# Output: 0x101ea3da0

In essence, these functions allow us to peek behind the scenes and get a glimpse of the inner workings of our Python programs, showing us the unique memory addresses where our data resides. This insight can be particularly useful when working on more complex programs or debugging issues related to variable assignments and memory management.