Python Variable Scope: Local, Global, Nonlocal and LEGB

Scope decides where a Python variable can be seen: a name created inside a function is local, and a name at the top of a file is global. Python looks names up in Local, Enclosing, Global, then Built-in scope, the LEGB rule.

rate = 0.0625              # global

def checkout(price):
    rate = 0.0825          # local: hides the global inside this function
    return price * (1 + rate)

These examples ran on Python 3.12.5, and the screenshots come straight from the Command Prompt. The tax rates are real Texas and Austin sales tax figures.

Below you’ll see each scope in action, when you need global and nonlocal, and the scope surprises that trip up people coming from other languages.

The LEGB rule: how Python looks up a variable

Every time you use a name, Python checks the scopes from the inside out and stops at the first match:

ScopeWhat lives thereExample
LocalNames assigned inside the current functionsubtotal in a function
EnclosingNames in any outer function, for nested functionstax_rate in checkout()
GlobalNames at the top level of the moduletax_rate = 0.0625
Built-inPython’s own nameslen, print, list

Here are all four in one short program. line_total() has no tax_rate of its own, so it finds the enclosing one before the global:

tax_rate = 0.0625                 # Global: the Texas state rate

def checkout(price):
    tax_rate = 0.0825             # Enclosing (for line_total): Austin's combined rate

    def line_total():
        return round(price * (1 + tax_rate), 2)   # Local first, then Enclosing

    return line_total()

print("checkout(100):", checkout(100))
print("global tax_rate:", tax_rate)       # untouched
print("len('Austin'):", len("Austin"))    # len lives in the Built-in scope

Output:

checkout(100): 108.25
global tax_rate: 0.0625
len('Austin'): 6
Command Prompt output of a Python LEGB example where a nested function uses the enclosing tax rate instead of the global one
The nested function used 8.25%, the enclosing value. The global 6.25% never changed.

Notice that the local assignment in checkout() didn’t touch the global. It created a new name that only exists inside that function.

Local variables only exist inside their function

A local variable is created when the function runs, and it’s gone when the function returns. Try to use it afterward and you get a NameError:

def order_total():
    subtotal = 59.97              # local to order_total
    return round(subtotal * 1.0825, 2)

print("order_total():", order_total())

try:
    print(subtotal)               # gone once the function returned
except NameError as err:
    print("NameError:", err)

Output:

order_total(): 64.92
NameError: name 'subtotal' is not defined
Command Prompt output showing a Python local variable raising NameError when accessed outside its function
subtotal existed only while order_total() was running.

If you need a value from inside a function, return it. Accessing variables outside a function covers the other options, and NameError: name is not defined explains the error itself.

Global variables in Python: reading vs assigning

Any function can read a global variable. Assigning to one is different: without the global keyword, an assignment creates a local variable instead.

visits = 0

def show_visits():
    print("reading a global is fine:", visits)

def add_visit():
    global visits                 # needed to ASSIGN to the global
    visits += 1

show_visits()
add_visit()
add_visit()
print("visits after two calls:", visits)

Output:

reading a global is fine: 0
visits after two calls: 2

Leave out global visits and add_visit() raises UnboundLocalError, because visits += 1 makes visits local before it has a value.

I keep globals for constants and configuration. For anything that changes, passing values in and returning them is easier to follow; setting global variables in functions has more examples.

The nonlocal keyword for nested functions

global skips straight to the module level. When the variable belongs to an outer function, you need nonlocal instead:

def make_cart():
    items = 0

    def add(qty):
        nonlocal items            # rebind the enclosing function's name
        items += qty
        return items

    return add

add = make_cart()
add(2)
print("items in cart:", add(3))

Output:

items in cart: 5

The inner function remembers items between calls; that’s how closures keep state without a global. Calling a function within a function shows more nested-function patterns.

Do if statements and for loops have their own scope in Python?

No, and that’s a surprise if you know C, Java or JavaScript. Only functions, classes, modules and comprehensions create a new scope:

# if and for blocks do NOT create a new scope
if True:
    status = "shipped"
print("set inside if :", status)

for i in range(3):
    pass
print("loop variable after the loop:", i)

# a comprehension DOES have its own scope
squares = [n * n for n in range(3)]
try:
    print(n)
except NameError as err:
    print("comprehension variable:", err)

Output:

set inside if : shipped
loop variable after the loop: 2
comprehension variable: name 'n' is not defined
Command Prompt output showing Python variables set inside if and for blocks are visible afterward while a list comprehension variable is not
status and i survived their blocks. The comprehension’s n didn’t.

So after for i in range(3), i is still 2. That’s handy sometimes, but it can also hide a bug if you reuse the name later and forget it already has a value.

List comprehensions are the exception: their loop variable stays inside. Python list comprehension goes deeper on that syntax.

Shadowing a built-in variable: ‘list’ object is not callable

Built-ins sit in the last scope Python checks, so any global with the same name hides them, and you won’t get a warning. Naming a variable list is the classic mistake:

list = ["Austin", "Denver"]       # hides the built-in list()

try:
    cities = list("Boston")
except TypeError as err:
    print("TypeError:", err)

del list                          # removes the global, so the built-in is visible again
print(list("Boston"))

Output:

TypeError: 'list' object is not callable
['B', 'o', 's', 't', 'o', 'n']
Command Prompt output of Python TypeError list object is not callable after a variable named list hides the built-in
Once a global called list exists, list("Boston") calls your list instead of the built-in.

del list fixes it in the moment, but the real fix is a better name. The same goes for str, dict, id, sum and input; Python naming conventions has safer options.

Class scope: why methods can’t see class variables directly

A class body has its own scope, but it isn’t an enclosing scope for the methods inside it. A method has to reach class variables through self or the class name:

class Store:
    tax = 0.0825

    def price_with_tax(self, price):
        try:
            return price * (1 + tax)          # a bare name doesn't see the class body
        except NameError as err:
            return f"NameError: {err}"

    def price_fixed(self, price):
        return round(price * (1 + self.tax), 2)   # go through self (or Store.tax)

shop = Store()
print(shop.price_with_tax(100))
print(shop.price_fixed(100))

Output:

NameError: name 'tax' is not defined
108.25

The bare name tax fails because the lookup goes from the method straight to the global scope, skipping the class. Class vs instance variables explains how self.tax finds it.

How to check if a variable is local or global

locals() and globals() return the names in each scope as dictionaries, so you can test membership directly:

tax_rate = 0.0625

def checkout(subtotal):
    shipping = 5.99
    print("locals():", locals())
    print("'tax_rate' in globals():", "tax_rate" in globals())
    print("'shipping' in globals():", "shipping" in globals())

checkout(40)

Output:

locals(): {'subtotal': 40, 'shipping': 5.99}
'tax_rate' in globals(): True
'shipping' in globals(): False

It’s useful for debugging, but you’ll rarely need it in normal code. If you find yourself checking whether a global exists, it’s usually a sign the value should be passed in instead.

Python variable scope: quick reference

You want toDo this
Read a global inside a functionJust use the name
Assign to a global inside a functionglobal name first
Assign to an outer function’s variablenonlocal name first
Use a class variable in a methodself.name or ClassName.name
Get a value out of a functionreturn it
Check where a name livesname in locals() / name in globals()
Avoid hiding a built-inDon’t name variables list, str, sum, id

For more on variables and functions, try these next:

Frequently asked questions

What is variable scope in Python?

Scope is the region of a program where a name can be used. Python has four: local, enclosing, global and built-in, searched in that order (the LEGB rule).

What’s the difference between local and global variables in Python?

A local variable is created inside a function and only exists while it runs. A global variable is defined at the top level of a module and can be read anywhere in it.

When do I need the global keyword?

Only when a function assigns to a global variable. Reading one needs no keyword; assigning without global creates a local instead.

What does nonlocal do in Python?

It lets a nested function assign to a variable in its enclosing function, rather than creating a new local one.

Do for loops create a new scope in Python?

No. The loop variable and anything assigned in the loop stay available after it ends. Functions, classes, modules and comprehensions create scopes; if and for blocks don’t.

Can a method use a class variable without self?

No. Use self.name or ClassName.name. The class body isn’t an enclosing scope for its methods; the Python tutorial on scopes and namespaces explains why.