In Python, you can't reassign a global variable within a function without the global keyword. However, accessing or modifying a global variable is fine.
Accessing global variable
x = 5
def print_x():
print(x)
print_x() # prints 5
When the Python interpreter encounters the x variable inside of the function print_x it first looks for it in the list of locals, and then when it doesn't find it, looks for it in the list of globals and finds its value, which is 5.
Modifying a global variable
As I mentioned, you can modify a global variable just fine without doing anything special:
arr = []
def modify_arr():
arr.append(5)
print(arr) # prints [5]
Similarly, when the Python interpreter reads arr.append(5) it looks up the variable arr in the locals list, doesn't find it, and then searches the globals list, finds it and calls its append method.
Defining a local variable
x = 5
def flawed_reassign_and_print_x():
x = 2
print(x)
flawed_reassign_and_print_x() # prints 2
print(x) # prints 5
When the interpreter sees x = 2 it adds a variable called x to the list of local variables with the value 2. Then when it encounters the x to print inside the function, as before it first looks x up in the list of locals and since it finds it, just prints out 2. It never gets to the step of looking for the global x since it found a local one first.
After the function call, x is unchanged.
Reassigning and accessing a global without the global keyword
x = 5
def very_flawed_print_and_reassign_x():
print(x)
x = 2
very_flawed_print_and_reassign_x() # raises an exception!
This is where I have to correct an explanation I gave in the previous section that wasn't quite the whole story. I said "When the interpreter sees x = 2 it adds a variable called x to the list of local variables". But if that were true, this function shouldn't crash, it should just print the global x and then create a local x.
However, what actually happens is that all local variable names are identified before the function runs. So it stores a variable x in the list of locals, but that it hasn't been assigned yet. So when it gets to print(x) it finds x in the list of locals and then raises an error because it hasn't been assigned yet. The very presence of a variable assignment inside a function makes that variable local.
Reassigning and accessing a global with the global keyword (the correct way)
x = 5
def change_and_print_x():
global x
x = 2
print(x)
change_and_print_x() # prints 2
print(x) # prints 2
This global declaration tells the interpreter that x should not be added to the list of locals, even though it sees an assignment of x within the function. So all changes update the x in the globals list.