Python has no Variables

Python doesn't have variables. It sounds mind-boggling, as one seems to use them all along. But really, that is not how it works with Python. This will really confuse people with a background in C or C++. If we do int n in C++, and write n = 1, n += 1 or the like, the address &n will never change. The value changes.

But in Python the “variables” are mere pointers to values somewhere. This can be seen because doing n += 1 in Python actually creates a new value. Expanding this as n = n + 1 and reading it as “let n point to the value of n + 1. We can easily see this by creating a variable and looking at the ememory address.

>>> n = 10000
>>> id(n)
140715441549616

Now increasing this number will make n point to a different address in memory!

>>> n += 1
>>> id(n)
140715441549392

The equivalent C++ code would be the following, which makes the steps apparent.

int *n = new int;
*n = 1000;
int *temp = new int;
*temp = *n + 1;
delete n;
n = temp;

Now, this seems pretty inefficient for integers. And it actually is, that's one of the reasons why Python usually performs slower than the C++ code that achieves the same things. But for small integers it is not that bad because there is an optimizations. The small integers are alle instantiated as singletons during startup, so there the addresses are re-used. See the following example:

>>> n = 1
>>> id(n)
140715560274080
>>> n = 2
>>> id(n)
140715560274112
>>> n = 1
>>> id(n)
140715560274080
>>> n += 1
>>> id(n)
140715560274112

In Java one has the explicit statement that every variable that holds an object is a pointer. Therefore all passing of objects is by-reference. In Python it is somewhat similar, but not quite.

For instance in C++ I could do the following to sort a vector that is handed to me:

void f(std::vector<int> &v) {
    std::sort(v.begin(), v.end());
}

Similarly I can do that in Python:

def f(v):
    v.sort()

In both cases it will sort the list in-place and the caller will get the list sorted. But what about deleting all elements? We can do that by assigning an empty list/vector to the variable.

void f(std::vector<int> &v) {
    v = std::vector<int>{};
}

Here the caller will find that all elements of the vector have been removed. But what about Python here?

def f(v):
    v = []

This will not chance the list of the caller! In Python there is no assignment, the = operator merely labels something. So in this case the caller's list will be unreachable within the function and the local “variable” v will just point to an empty list. Basically we are doing this in C++:

void f(std::vector<int> *v) {
    v = new std::vector<int>{};
}

And in Python there is just no way to write this instead:

void f(std::vector<int> *v) {
    *v = std::vector<int>{};
}

Understanding this very nuanced mechanism in Python is necessary to solve some rather strange looking bugs in Python programs.