Variable scope in Python: where a name exists and where it stops

Scope says where a name exists in Python and where it stops: the LEGB rule, the global and nonlocal keywords, and the blocks that open nothing at all.
5 min read
Believemy logo

Definition

Write a function and give one of its variables a name already used elsewhere in the file, a total, a counter. Will the two collide? That is the question scope answers: the region of the program where a name stands for one specific thing, before it becomes free again somewhere else.

In Python that region is not carved out line by line but block by block. Every function, every class and every file opens a fresh namespace: whatever is born inside stays there, and disappears once the block ends.

PYTHON
message = "I come from the module"

def show():
    message = "I come from the function"
    print(message)

show()
print(message)

# I come from the function
# I come from the module

The two message names look alike without being the same thing. The one inside the function is born on the call and dies on the return, never touching the one above. That watertightness is what answers the opening question: you pick whichever name suits you, without rereading the file.


The LEGB rule

A function is never fully isolated, though: it also uses names it did not create itself, a module constant, a function Python supplies. Faced with one of those names, how does it know where to look?

Python looks in four spaces, always in the same order, and stops at the first that answers. The initials of those spaces form the acronym LEGB. The table below details what each level holds.

LevelWhat it holdsTypical example
LocalNames created in the running functionA parameter, a working variable
EnclosingNames of the function wrapping this oneThe counter of a closure
GlobalNames of the module, written unindentedA configuration constant
Built-inThe names Python supplies by defaultlen, print, sorted

The first level that answers wins, without Python checking the rest. If all four stay silent, a NameError is raised.

Warning

Naming a variable list, sum or type works without the slightest error, since Local is checked before Built-in. But the built-in function becomes invisible for the rest of the block: the next call to list(...) fails with a TypeError, far from the real cause.

The diagram below shows that path: a read that stops at the module, never reaching the built-in names.

How a name is looked up: the LEGB ruleFour nested levels, from local out to built-ins. A single arrow leaves the reading point, crosses the local scope then the enclosing function, and stops at the module, the first level that holds the name. Built-ins are never reached.B · Built-innever reachedG · Modulethe name is hereE · Enclosingnot hereL · Localnot hereprint(total)The search stops at the first level that answers.


Reading climbs, writing stays put

A read follows the path just described, from local out to built-ins. But what happens when a variable is written to, rather than read? One could assume an assignment behaves like a read, and reaches for the existing name wherever it lives to change it in place.

That is not how Python works. An assignment never climbs: it creates or replaces a name in the block where it is written. That asymmetry alone explains nearly every surprise scope has in store.

PYTHON
total = 0

def add(amount):
    total = total + amount   # UnboundLocalError

def add_fixed(amount):
    global total
    total = total + amount

The line total = total + amount looks like it first reads total, then writes it. But Python reads the whole body before running it: the moment it spots an assignment to total, it decides that name is local for the whole function, including where it reads it before writing it. Hence the UnboundLocalError.

Two keywords still allow writing to the outside: global for the module, and nonlocal for the enclosing function. Reaching for them is rarely the best choice: a function that returns a value reads better than one that edits shared state.


The blocks that open no scope

In many languages a new scope opens at every brace: a variable declared inside an if disappears once the block ends. Coming from those languages, it is easy to assume Python behaves the same way.

It does not. Only functions, classes and modules open a scope in Python. A condition, a loop or an error-handling block share the scope that contains them, as here.

PYTHON
for client in clients:
    discount = compute(client)

print(client)
print(discount)

Both names stay readable after the loop, which is handy when intended and dangerous when it is not.

Good to know

On an empty list the loop never runs once: neither client nor discount gets created, and the next line raises a NameError that only shows up on that precise list.

The one exception to this porosity is the variable of a list comprehension, which stays locked inside the comprehension and never spills into what follows.


Frequently asked questions

Question

Why is the global keyword discouraged?

Because it makes a function's result depend on the module's state rather than on its arguments: two identical calls can then give two different answers. Returning the value stays preferable in most cases.

Question

Is the body of a class visible from its methods?

No, and that is a frequent surprise. The class body does have its own scope, but it only lives for the duration of the definition: a method does not see it as an enclosing level. An attribute is read through self, never as an ordinary variable.

Question

How can the visible names at a given point be listed?

The built-in functions locals() and globals() hand back the names reachable where they are called, which settles a doubt without starting a debugger.

Related terms

Discover our python glossary

Browse the terms and definitions most commonly used in development with Python.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.