How to use a global variable in a Python function
Reading a module-level variable inside a function needs nothing at all.
Assigning to one needs the global keyword — and the error you get without it
points at the wrong line, which is what makes this confusing.
Reading is free
counter = 0
def read_it():
return counter
read_it() : 0
Python looks in the local scope, then the enclosing one, then the module, then the built-ins. No declaration required.
Assigning creates a local instead
def assign_it():
counter = 99
return counter
inside : 99 outside afterwards : 0
The function made its own counter, used it, and threw it away at the end. The
module-level one never changed.
This is not a special rule about globals. It is the ordinary rule that assignment binds a name in the current scope.
The error that gives it away
Here is where it gets interesting. Read the variable and assign to it:
def read_then_assign():
print(counter) # line 1
counter = 1 # line 2
-> UnboundLocalError: cannot access local variable 'counter' where it is not associated with a value
The read failed. Not the write.
Python decides at compile time which names are local, and one assignment
anywhere in the body makes the name local for the entire body — including
lines above it. So by the time line 1 runs, counter is a local that has not
been assigned yet.
That is why the error looks so misleading: you get it on a line that only reads, because of a line further down.
global
def with_global():
global counter
counter = 99
after with_global() : 99
global tells the compiler not to treat the name as local. Put it at the top
of the function.
You often do not need it
This is the part worth internalising. global is about rebinding the name,
not about changing the object. Mutating works without it:
items = []
registry = {}
def mutate():
items.append("x")
registry["k"] = "v"
items after append : ['x']
registry after : {'k': 'v'}
No assignment to items or registry happens — items.append(...) is a
method call, and registry["k"] = ... assigns to an item, not to the name.
So there is nothing for Python to make local.
Rebind the name and you need it again:
def rebind():
global items
items = ["rebound"]
Which is the usual workaround: keep a module-level dict or a small object and mutate it, rather than reassigning module-level names from all over the code.
config = {"debug": False}
def enable_debug():
config["debug"] = True
config after enable_debug() : {'debug': True}
nonlocal is a different keyword
global jumps all the way to module level. For a name in an enclosing
function, you want
nonlocal:
def outer():
total = 0
def add(n):
nonlocal total
total += n
add(3); add(4)
return total
outer() with nonlocal : 7
without nonlocal : UnboundLocalError: cannot access local variable 'total' ...
'global' does NOT reach the enclosing function : 0
The third line is the one to note. Using global inside a nested function does
not reach the enclosing function — it creates or rebinds a module-level name,
and the enclosing total is untouched.
Modules and the import form
One more place this bites. These two are not equivalent:
from config import value # copies the current binding
import config # then read config.value
from ... import binds the value to a name in your module. If the other
module later rebinds it, you will not see the change. import config followed
by config.value reads the attribute every time, so you always get the current
one.
Should you?
Mostly no. A function that reads and writes module-level state is harder to test, cannot be called twice safely, and gives no hint in its signature about what it touches.
Pass values in and return them out. Where genuinely shared state is needed — a
cache, a connection pool, a configuration object — a module-level container
that you mutate is honest about it, and does not need global at all.
About Netcup (advertisement)
The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).