Tutorials Logic, IN info@tutorialslogic.com

Scopes in Python LEGB Rule

Scope Basics

Scope decides where a name can be found and changed in Python code.

Python searches names using the LEGB idea: local, enclosing, global, then built-in.

Most scope bugs happen when a variable is created in one place and read from another place where Python cannot see it.

Python Scope

Scope determines where a variable is accessible. Python uses the LEGB rule to resolve variable names - it searches in this order:

  • Local - inside the current function
  • Enclosing - in any enclosing functions (closures)
  • Global - at the module (file) level
  • Built-in - Python's built-in names (print, len, etc.)

Local Scope

Local Variables Inside a Function

Local Variables Inside a Function
def my_function():
    x = 10   # local variable - only exists inside this function
    print(x)

my_function()   # 10
# print(x)      # NameError: name 'x' is not defined

# Each function call gets its own local scope
def counter():
    count = 0
    count += 1
    return count

print(counter())  # 1
print(counter())  # 1 (fresh scope each call)

Global Scope

Read and Update Module Variables

Read and Update Module Variables
message = "Hello"   # global variable

def greet():
    print(message)  # can READ global variable

greet()   # Hello

# To MODIFY a global variable inside a function, use 'global'
count = 0

def increment():
    global count
    count += 1

increment()
increment()
print(count)   # 2

# Without 'global', Python creates a new local variable
def bad_increment():
    count = count + 1  # UnboundLocalError!

Enclosing Scope

When a nested function references a variable from its enclosing function, it creates a closure.

Closures

Closures
def outer(x):
    def inner(y):
        return x + y   # 'x' is from enclosing scope
    return inner

add5 = outer(5)
add10 = outer(10)

print(add5(3))    # 8
print(add10(3))   # 13

# Closure remembers the enclosing scope
def make_counter():
    count = 0
    def increment():
        nonlocal count   # modify enclosing variable
        count += 1
        return count
    return increment

counter = make_counter()
print(counter())  # 1
print(counter())  # 2
print(counter())  # 3

# Each call to make_counter() creates an independent counter
counter2 = make_counter()
print(counter2()) # 1 (independent)

nonlocal Keyword

nonlocal

nonlocal
def outer():
    x = 10

    def inner():
        nonlocal x   # refers to outer's x
        x = 20
        print(f"inner: x = {x}")

    inner()
    print(f"outer: x = {x}")   # x was modified by inner

outer()
# inner: x = 20
# outer: x = 20

# LEGB in action
x = "global"

def outer():
    x = "enclosing"

    def inner():
        x = "local"
        print(x)   # local

    inner()
    print(x)       # enclosing

outer()
print(x)           # global

Built-in Scope

Built-in Names

Built-in Names
import builtins

# See all built-in names
print(dir(builtins))

# Built-ins are always available
print(len([1, 2, 3]))   # 3
print(type("hello"))    # <class 'str'>
print(range(5))         # range(0, 5)

# You can shadow built-ins (but don't!)
# list = [1, 2, 3]   # now 'list' is a variable, not the built-in!
# list([1, 2])        # TypeError!

# Check if a name is a built-in
print(hasattr(builtins, "print"))   # True
print(hasattr(builtins, "myvar"))   # False
Scope readiness check

Can You Predict Name Lookup?

3 checks
  • Scope determines where a variable is accessible.
  • Python uses the LEGB rule to resolve variable names - it searches in this order.
  • A closure lets an inner function remember values from its outer function.

Name Lookup Surprises

  • Changing a global from inside a function

    Return a new value instead of mutating global state unless global is truly needed.
  • Shadowing built-in names

    Avoid names such as list, dict, str, input, or sum for your own variables.
  • Expecting local variables outside a function

    Pass values out with return if another part of the program needs them.

Try this next

Predict the Name Used

0 of 3 completed

  1. Create a global name and a local name with the same spelling, then print both carefully.
  2. Rewrite a function that changes a global counter so it returns the new count.
  3. Rename a variable called list and confirm list() works again.

Questions About Scope

LEGB means local, enclosing, global, and built-in. Python checks names in that order.

Use global rarely. Passing values as parameters and returning results is usually clearer and easier to test.

nonlocal lets an inner function change a variable from the nearest enclosing function scope.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.