Tutorials Logic
Tutorials Logic, IN info@tutorialslogic.com
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Website Development
Practice
Quiz Challenge Interview Questions Certification Practice
Tools
Online Compiler JSON Formatter Regex Tester CSS Unit Converter Color Picker
Compiler Tools

Scopes in Python — LEGB Rule Explained

What is 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 Scope
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

Global Scope
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 & Closures

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

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

Ready to Level Up Your Skills?

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