Python Scope
Learn Python scope and the LEGB rule: local, enclosing, global, and built-in scopes with clear examples, gotchas, and the global/nonlocal keywords.
Understanding Python Scope
Scope determines where a variable name is visible and how long it lives. Every name you create — a variable, a function, a class — belongs to exactly one scope. When Python encounters a name, it searches scopes in a strict order until it finds the name or raises a NameError.
This page covers:
- The four scope levels and the LEGB lookup rule
globalandnonlocalkeywords- Common pitfalls: shadowing,
UnboundLocalError, and name leakage - How to inspect scopes at runtime
The LEGB Rule
Python resolves names in four layers, searched from innermost to outermost:
| Level | Stands for | What it contains |
|---|---|---|
| L | Local | Names defined inside the current function |
| E | Enclosing | Names in any enclosing (outer) function, for nested functions |
| G | Global | Names defined at the top level of the current module |
| B | Built-in | Names pre-loaded by Python — print, len, range, type, etc. |
Python stops at the first match. If no match is found in any layer, a NameError is raised.
Local Scope
A name is local when it is assigned inside a function. It is created when the function is called and destroyed when the function returns. Local names are invisible outside the function.
def greet():
message = "Hello, World!" # local variable
print(message) # accessible here
greet()
# Output: Hello, World!
print(message) # NameError: name 'message' is not definedWhy local scope matters
Local scope lets you use simple, descriptive names like total, result, or i inside every function without any of them clashing with each other or with module-level code.
def calculate_area(radius):
pi = 3.14159 # local to this function
area = pi * radius ** 2
return area
def calculate_volume(radius, height):
pi = 3.14159 # completely separate local 'pi'
volume = pi * radius ** 2 * height
return volume
print(calculate_area(5)) # 78.53975
print(calculate_volume(5, 10)) # 785.3975Both functions use pi independently. There is no conflict.
Enclosing Scope
When a function is defined inside another function, the inner function can read names from the outer (enclosing) function. This is the E in LEGB.
def outer():
color = "blue" # enclosing variable
def inner():
print(color) # reads from enclosing scope
inner()
outer()
# Output: blueThe inner function can read color without any special keyword because Python walks up to the enclosing scope automatically.
Modifying an enclosing variable with nonlocal
Reading an enclosing variable is automatic, but assigning to it requires the nonlocal keyword. Without it, Python treats the assignment as creating a new local variable, not modifying the enclosing one.
def make_counter():
count = 0 # enclosing variable
def increment():
nonlocal count # declare intent to modify the enclosing 'count'
count += 1
return count
return increment
counter = make_counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3Each call to counter() modifies the same count variable. This pattern is the basis of closures — see Python Closures for a deeper look.
Global Scope
Names defined at the top level of a module (outside any function) are global. They are accessible from anywhere in the same module, including inside functions.
language = "Python" # global variable
def display_language():
print(language) # reads global without any keyword
display_language()
# Output: PythonModifying a global variable with global
You can read a global variable from inside a function without any keyword. But if you want to assign to it, you must declare it with global first:
total = 0 # global variable
def add_to_total(n):
global total # declare intent to modify the global
total += n
add_to_total(5)
add_to_total(3)
print(total) # 8Without global total, the line total += n would raise an UnboundLocalError because Python would treat total as a local variable that has not been assigned yet.
For more on global variables see Python Global Variables.
When to avoid global
Using global extensively makes code harder to test and reason about. Prefer returning values from functions and passing data as arguments. Reserve global for genuine module-level state — configuration flags, counters, or caches — and keep it rare.
Built-in Scope
The built-in scope contains names that Python provides automatically. They are available in every module without any import.
Common built-in names include print, len, range, type, int, str, list, dict, set, tuple, open, input, max, min, sum, sorted, enumerate, zip, and map.
Do not shadow built-in names
Because the built-in scope is searched last, any local or global name with the same spelling will shadow it. This is a common mistake:
# Bad: shadows the built-in 'list'
list = [1, 2, 3]
print(type(list)) # <class 'list'> — still works here
new = list([4, 5]) # TypeError: 'list' object is not callableDelete the shadowing name to restore the built-in:
del list # remove the local/global shadowing name
new = list([4, 5]) # works again: [4, 5]The built-in module is available as builtins if you ever need to access it explicitly:
import builtins
print(builtins.len([1, 2, 3])) # 3Common Gotchas
UnboundLocalError
The most frequent scope mistake in Python is reading a name before assigning it inside a function when there is also an assignment to that name anywhere in the same function:
x = 10
def bad_func():
print(x) # UnboundLocalError! Python sees x = 20 below
x = 20 # this makes 'x' local for the whole function
bad_func()Python decides at compile time that x is local (because it is assigned in the function). The attempt to read it before the assignment raises UnboundLocalError: local variable 'x' referenced before assignment.
Fix it by either declaring global x or by not assigning to x inside the function.
Variable shadowing
A local variable that shares a name with a global silently shadows the global. This is usually intentional, but it can cause subtle bugs when you expect to be reading the global:
x = 10 # global
def my_func():
x = 20 # local — shadows the global
print(x) # 20, not 10
my_func()
print(x) # 10 — global unchangedInspecting Scope at Runtime
Python exposes the current scope contents through two built-in functions.
locals() returns a dictionary of the current local scope (or the module-level namespace if called at the top level):
def show_locals():
a = 1
b = "hello"
print(locals()) # {'a': 1, 'b': 'hello'}
show_locals()globals() returns the global (module-level) namespace dictionary:
language = "Python"
print("language" in globals()) # TrueThese are useful for debugging and introspection but should not be used to manipulate variables dynamically in production code.
Scope and Functions
Functions in Python are objects and are themselves names that live in some scope. A function defined at the top of a module is global; a function defined inside another function is local to the enclosing function.
def outer():
def helper(): # helper is local to outer
return 42
return helper()
print(outer()) # 42
# helper() # NameError: helper is not defined hereSee Python Functions for how functions are defined, and Python Lambda for anonymous functions that also obey the LEGB rule.
Quick Reference
| Keyword | Effect |
|---|---|
| (none) | Read from the nearest enclosing scope by LEGB lookup |
global x | Tell the current function that x refers to the module-level name |
nonlocal x | Tell the current function that x refers to the name in the nearest enclosing function |