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, ...).