The Sharat's

The Weird global

Python’s global keyword allows us to change the value of module-level variables inside functions. Sounds so simple and useful, doesn’t it? Well, yeah. I’m going to show you how it can be useful in the simple sense and situations where it can drive people nuts.

Simple Usage

Consider the following top.py script. We have a single module-level (aka global) variable here, and we change its value in the function done.

top.py
1
2
3
4
5
6
7
8
9
10
11
are_we_done = False


def mark_done():
    global are_we_done
    are_we_done = True


print("Done?", are_we_done)
mark_done()
print("Done?", are_we_done)

Running this, we get the following output:

Done? False
Done? True

The reason we were able to change the value of the global variable are_we_done from inside the mark_done function is because we declared it as such on line 5. If that declaration isn’t there, we’d just be defining a new function level variable called are_we_done inside the mark_done function. Which is not what we wanted.

Refer Directly

Note that declaring variables as global is needed only when we’re modifying the value of the variable. That means if we are only accessing the variable, we don’t need to declare it as global. This is how capitalized constant variables work in most Python scripts:

CURRENT_PLANET = "Earth"


def get_moon_count():
    if CURRENT_PLANET == "Earth":
        return 1
    else:
        raise ValueError("No idea!")


print(get_moon_count())

This, of course, prints out 1. Here, we are using the CURRENT_PLANET global variable inside the function without declaring it as global. Accessing doesn’t require explicitly declaring as global.

Modifying the Referred Object

A small note on the terms we’ve been using here. Accessing doesn’t require global declaration, but modifying does. Now look at the following code snippet:

CALLS = []


def record_call(phone_number):
    CALLS.append(phone_number)


record_call("123-45-678")
record_call("987-65-432")
print(CALLS)

Here, since we are appending to the CALLS list, is that considered modifying the global variable? The answer is no. We are merely accessing the CALLS variable’s value, which happens to be a list, on which we call the .append method. There’s no modifying going on here so far. The .append method, however, will change the state of the list object. But for the purposes of using the CALLS variable here, we are only accessing it. So, we don’t need to declare it as global.

So what does modifying mean? Simply put, if you want to reassign a global variable, it’s considered as modifying.

Assigning without Declaring

This behaviour of global variables causes some slightly unintuitive situations. For example, consider the following piece of code:

1
2
3
4
5
6
7
8
is_server_up = False


def mark_server_up():
    print(is_server_up)


mark_server_up()

In this script, we are using the global variable is_server_up on line 5, without declaring it as global, and it works fine. Now, we add another line to this function:

1
2
3
4
5
6
7
8
9
is_server_up = False


def mark_server_up():
    print(is_server_up)
    is_server_up = True


mark_server_up()

If we run this script, we get the following error:

Traceback (most recent call last):
  File "/check.py", line 9, in <module>
    mark_server_up()
  File "/check.py", line 5, in mark_server_up
    print(is_server_up)
UnboundLocalError: local variable 'is_server_up' referenced before assignment

Okay, we kind of expected an error because we are trying to modify a global variable without declaring it. But note that the error comes from line 5, not on line 6, where we are modifying the variable. The error message gives a hint on what’s happening.

local variable 'is_server_up' referenced before assignment

Since we didn’t declare is_server_up as global, and since we are setting a value to is_server_up, Python decided that we want a local variable in our function with the same name. With that understanding, it looks like we are referencing the is_server_up local variable before assigning a value to it. That’s the error we see here.

Conclusion

Global variables have their place, but, if it’s not for constant-like values, I’d recommend against using global variables at all. It might make sense for small one-off scripts, and when it does, keep the above small details in mind.