Dark theme

Understanding variables


There are two key things to understand about variables in Python. The first is that they are a combination of a label and an object and, often as not, they can be best considered to be totally recreated when assigned a new object (though the truth of this varies). The second is that objects can be divided into those which changable contents (mutable variables) and those with unchangable contents (immutable variables).


Variables recreated on assignment

Let us first consider that variables are recreated on assignment (the attaching of a value to a label). This is rarely troublesome, but we can see it in action when we change a variable inside a method. Here's some strange behaviour:

a = 10
def f1 ():
    a = 20
    print(a) # Prints 20

print(a) # This would print 10.
f1()
print(a) # This would print 10.

We might expect the final line to print 20, as a has been changed to that inside f1, however, as soon as we assign a = 20, this becomes a new variable whose "scope" (place where it can be seen) is f1, entirely separate from the a at the and bottom of the file. Worse, this becomes true everywhere in the function, so were we to use it inside the function above the new assignment (for example, to print it first), this code would break, the interpreter saying the variable hadn't yet been set up.

The solution is to use the global keyword (if the variable is first used outside any functions), or the nonlocal keyword (if the variable is first used in a function the current function is embedded in), to say that you want to use the same label, not a new one with the same name but a different scope:

a = 10
def f1 ():
    global a
    a = 20
    print(a) # Prints 20

print(a) # This would print 10.
f1()
print(a) # This would print 20 now.


Mutable and immutable variables

Note that this recreation applies as much to mutatable variables as immutatable variables. For this reason, it is perhaps better to talk about the contents of variables being mutatable or immutatable, rather than the variables themselves, as we see here:

a = 10
b = [10]
c = [10]

def f1():
    a = 11 # A new number.
    b = [11] # A new list.
    c[0] = 11 # Changing the content of a list.

f1()
print(a) # Prints 10 .
print(b) # Prints [10] .
print(c[0]) # Prints 11 .

Changing the content of the primary object the variable is attached to is fine, but changing the actual primary object is not; this creates a new variable.

A notable place where the distinction becomes important is in the allocation of default variables in functions. If we have the following:

def counter(c1 = 0, c2 = [0]):
    c1 = c1 + 1
    c2[0] = c2[0] + 1
    print (c1)
    print (c2)

counter()
counter()
counter()

The output we get is:

1 [1]
1 [2]
1 [3]

To understand this, we need to know that all default variables are created when the interpreter first opens the file. We might consider that we assign c1 = c1 + 1 this creates a new c1 (as it does). This isn't the case with changing the content of c2 as we're only changing its content.

As it happens, there's also something more complicated going on as well, as placing a print(c1) at the top of the function would show. This just prints zeros. Why is this odd, if the above is the whole explanation? What should we see on the second run through? It seems likely that they interpreter actually remakes variables with immutable contents each time the function runs at the start of the function.


Variables a label and an object

This is perhaps best seen in use when a function is passed data. Here all that happens is a label attached to a object outside the function is analysed, and a new label attached to the same object inside the function:

first_label = [1,2,3]

def f1(second_label):
    print(second_label) # Prints [1,2,3] .

f1(first_label) # Call the function.

It can also be seen in the use of global and nonlocal, above, where these keywords prevent the creation of a new label with the same name for a new purpose and with a new scope.