Free demoJob-oriented courses at Debugshala, Old Palasia, Indore. Freshers, non-IT and year-gap welcome.Book a free demo +91-8982385539

Python Variable Scope: LEGB Rule, global and nonlocal Explained

Lesson 30 of 52 · 9 min · Updated 13 Nov 2026

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 variable scope LEGB rule shown as nested boxes: local inside enclosing inside global inside built-in

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
Python
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?

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.

Python
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?

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.

Python
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.

Python global keyword compared: without global an assignment creates a local copy, with global it rebinds the module variable

Without the keyword, assignment makes a new local; with it, the module-level name is updated.

Python
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.

Python
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.

Python
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.

Python
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.

Python
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, list or max break the functions you need later.
  • Assuming blocks have scope. Variables created inside if or for stay visible after the block.
  • Using nonlocal for a module variable. It only reaches enclosing functions; use global for 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 global to rebind a module variable, nonlocal for an enclosing function's variable.
  • Mutating a shared list or dict needs no keyword, but hides side effects.
  • Don't name variables sum, list or max.
  • Loops don't create scope; functions and comprehensions do.

Next, see how scope extends to whole files in Python modules and imports.

Take the free Python Programming quiz10 quick questions with explanations. No sign-up needed, and you can come back to this lesson after.

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

Python Recursion: Base Case, Call Stack and Nested DataPython Modules and Imports: import, from, as and __name__