Python Variable Scope: LEGB Rule, global and nonlocal Explained
Learn Python variable scope with the LEGB rule: local, enclosing, global and built-in names, the global and nonlocal keywords, and the UnboundLocalError trap.
Picture a coaching institute building in Old Palasia. Each classroom has its own whiteboard. The floor has a shared notice board in the corridor. The ground-floor lobby has a board for the whole building. And outside, the city has rules everyone already knows, like which side of the road to drive on.
When a trainer in Room 3 says "today's topic", students look at Room 3's whiteboard first. If nothing is written there, they check the corridor board, then the lobby, then fall back on common knowledge. Writing on Room 3's whiteboard never changes the lobby board.
Python looks up variable names in exactly this way. The classroom whiteboard is a function's local scope. The corridor is an enclosing function. The lobby is the module's global scope. The city rules are Python's built-in names such as print and len.
Variable scope in Python is the region of a program where a name is visible; Python resolves each name by searching the local, enclosing, global and built-in scopes in that order, known as the LEGB rule.
This lesson follows Python recursion and assumes you can write Python functions. Examples target Python 3.12+.
What is the LEGB rule in Python?
LEGB names the four scopes Python searches, from the innermost outwards. The search stops at the first scope that has the name. If none has it, you get a NameError.
Python searches from the innermost box outwards and uses the first match it finds.
| Scope | Where it lives | Example |
|---|---|---|
| Local | Inside the function being run | a variable assigned in the function body |
| Enclosing | Inside an outer function, for nested functions | a helper defined inside another function |
| Global | At the top level of the module (the .py file) |
GST_RATE = 0.18 near the imports |
| Built-in | Provided by Python itself | print, len, sum, max |
GST_RATE = 0.18 # global
def invoice_total(amount):
discount = 50 # local
return round((amount - discount) * (1 + GST_RATE)) # reads the global
print(invoice_total(1000))
print(len("Indore")) # len comes from the built-in scope
try:
print(discount)
except NameError as err:
print("NameError:", err)discount exists only while invoice_total runs. Once the function returns, its local scope is thrown away, so the last line fails. GST_RATE is found in the global scope because there is no local variable with that name.
Quick check
In which order does Python search for a variable name?
The LEGB rule: Python checks the innermost scope first and moves outwards, stopping at the first match.
Local vs global variables in Python
A local variable is created when you assign to a name inside a function. Each call gets a fresh set of locals, which is why two calls never interfere with each other.
A global variable is assigned at the top level of a file. Every function in that file can read it. This is fine for true constants, written in capitals by convention, such as GST_RATE or BRANCHES.
city = "Indore" # global
def set_city():
city = "Bhopal" # a NEW local, not the global
print("inside:", city)
set_city()
print("outside:", city)
def report(region):
region = region.upper() # parameters are locals too
return region
r = "ujjain"
print(report(r), r)The assignment inside set_city did not touch the global. It created a separate local city that happened to share the name. Parameters behave the same way: reassigning region inside the function leaves the caller's r unchanged.
Quick check
A function contains count = count + 1 and count is a global. What happens if there is no global statement?
The assignment makes count local for the whole function, so reading it before it has a value fails.
Why do I get UnboundLocalError?
This is the most confusing scope error for beginners. Python decides which names are local before the function runs, by scanning the whole body. If a name is assigned anywhere in the function, it is local everywhere in that function.
orders_processed = 0
def process(order):
try:
orders_processed = orders_processed + 1 # read before local exists
except UnboundLocalError as err:
print("UnboundLocalError:", err)
return order
process({"id": 101})
print("Still:", orders_processed)The line reads orders_processed on the right and assigns it on the left. Because of the assignment, Python treats the name as local, and the local has no value yet. The global with the same name is never consulted.
How do you use the global keyword in Python?
global name tells Python that assignments to name in this function should rebind the module-level variable instead of creating a local.
Without the keyword, assignment makes a new local; with it, the module-level name is updated.
orders_processed = 0
def process(order):
global orders_processed
orders_processed += 1
return order["amount"]
for o in [{"amount": 1450}, {"amount": 980}, {"amount": 2300}]:
process(o)
print("Processed:", orders_processed)
# A cleaner design: return the result instead of changing a global
def count_orders(orders):
return len(orders)
print("Counted:", count_orders([1450, 980, 2300]))global works, but it couples the function to a variable it does not own. A function that takes inputs and returns outputs is easier to test, reuse and run in parallel. Treat global as a sign that the design could be simpler.
Changing a global list without the keyword
You only need global to rebind a name, meaning to point it at a new object with =. Calling a method that changes the object in place, such as .append() or [key] = value, is not a rebinding.
daily_log = []
totals = {"Indore": 0}
def record(city, amount):
daily_log.append((city, amount)) # mutates, no global needed
totals[city] = totals.get(city, 0) + amount
record("Indore", 1200)
record("Dewas", 640)
print(daily_log)
print(totals)This is convenient but hides side effects. Someone reading record("Indore", 1200) cannot tell it changes two shared objects. The function arguments lesson shows the related mutable default trap.
What does nonlocal do in Python?
Nested functions add the enclosing scope. An inner function can read variables of the function around it. To rebind one of them, the inner function declares it nonlocal.
def make_tracker(target):
total = 0
def add(amount):
nonlocal total # the enclosing function's total
total += amount
return f"Rs {total:,} of Rs {target:,} ({total / target:.0%})"
return add
week = make_tracker(50000)
print(week(12000))
print(week(18500))
print(week(9000))Each call to week updates the same total, which lives on after make_tracker has returned. A function that remembers variables from its enclosing scope is a closure. They get their own lesson in Python closures.
The difference between the two keywords is the scope they reach: global jumps straight to the module level, while nonlocal targets the nearest enclosing function. nonlocal cannot refer to a global.
Built-in scope and shadowing
Built-in names live in the outermost scope, so any global or local with the same name hides them. Analysts often do this by accident with sum, max, list, id or type, which are natural column names.
sales = [1200, 950, 1800]
sum = 0 # shadows the built-in sum()
for s in sales:
sum += s
print("Manual total:", sum)
try:
print(sum(sales))
except TypeError as err:
print("TypeError:", err)
del sum # remove the global; built-in is visible again
print("Built-in total:", sum(sales))The fix is simple: choose names such as total, max_sale or customer_id. If you have already shadowed a name in a notebook, del the variable or restart the kernel.
Loops and comprehensions
for loops and if blocks do not create a new scope in Python. A loop variable still exists after the loop ends. Comprehensions do have their own scope, so their variable does not leak.
for month in ["Jan", "Feb", "Mar"]:
pass
print("Loop variable after the loop:", month)
squares = [n * n for n in range(3)]
print(squares, "n defined outside?", "n" in globals())Common mistakes with Python variable scope
- Expecting an assignment inside a function to change a global. It creates a local unless you use
global. - Reading and assigning the same name in a function. This raises
UnboundLocalError. - Overusing
global. Return values instead; the function becomes testable. - Shadowing built-ins. Variables named
sum,listormaxbreak the functions you need later. - Assuming blocks have scope. Variables created inside
iforforstay visible after the block. - Using
nonlocalfor a module variable. It only reaches enclosing functions; useglobalfor module level.
Key takeaways
- Python resolves names using LEGB: Local, Enclosing, Global, Built-in.
- Assigning a name inside a function makes it local to that function.
- Use
globalto rebind a module variable,nonlocalfor an enclosing function's variable. - Mutating a shared list or dict needs no keyword, but hides side effects.
- Don't name variables
sum,listormax. - Loops don't create scope; functions and comprehensions do.
Next, see how scope extends to whole files in Python modules and imports.
FAQ
What is the scope of a variable in Python?
The scope of a variable is the part of a program where its name can be used. A name assigned inside a function is local to it, a name assigned at the top of a file is global to that module, and built-in names like print are available everywhere. Python searches these scopes using the LEGB rule.
What is the difference between local and global variables in Python?
A local variable is created inside a function and disappears when the function returns. A global variable is defined at the top level of a module and is readable by every function in it. Functions can read globals freely, but assigning to one requires the global keyword, otherwise Python creates a local instead.
When should I use the global keyword in Python?
Use global only when a function must rebind a module-level name, such as a counter in a short script. In most code, passing values as arguments and returning results is clearer and easier to test. Changing a global list or dict in place with methods like append does not need the keyword.
What is the difference between global and nonlocal in Python?
global makes assignments inside a function rebind a variable at the module's top level. nonlocal makes them rebind a variable in the nearest enclosing function, which is only meaningful in nested functions. nonlocal cannot reach module-level variables, and global skips enclosing functions entirely.
How can I learn Python functions and scope with a trainer in Indore?
Debugshala runs offline data analytics batches at its Old Palasia campus in Indore, covering Python functions, scope, pandas and SQL, in English and Hindi. A free demo class is available before you pay. Details are on the page for Debugshala's analytics classes at Old Palasia, and you can test your Python scope knowledge with MCQs on Debugshala Test.
From the team behind this site
Go further with the Data Analytics Course at Debugshala
These tutorials are written by Debugshala, a training and placement institute at Old Palasia, Indore. The classroom course adds live mentoring, real projects and interview preparation.
- Offline classes at Old Palasia, Indore
- Free demo class before you pay
- Non-IT, fresher and year-gap friendly
- Placement assistance
700+ students placed · 200+ hiring partners · 4.9★ Google rating