LearnContact
Lesson 2525 min read

Variable Scope

Understand local, enclosing, and global scope in Python, along with the global and nonlocal keywords and the LEGB rule.

Introduction

Every variable in Python lives inside a scope — a region of code where that variable is visible and usable. A variable created inside a function is not automatically available outside of it, and understanding exactly why is essential for avoiding some of the most confusing bugs beginners run into.

This lesson covers local and global scope, the global and nonlocal keywords, and the LEGB rule Python uses to figure out which variable a name actually refers to.

What You Will Learn
  • The difference between local and global scope.
  • How to modify a global variable from inside a function using global.
  • How nested functions see variables in an enclosing scope, and how nonlocal works.
  • The LEGB rule Python follows to resolve variable names.
  • Why shadowing a global name inside a function can cause subtle bugs.

Local Scope

A variable created inside a function belongs to that function's local scope. It only exists while the function is running, and it cannot be accessed from outside the function at all.

local_scope.py
def calculate_total():
    total = 100  # local variable
    print("Inside function:", total)

calculate_total()
print(total)
Output
Inside function: 100
Traceback (most recent call last):
  ...
NameError: name 'total' is not defined

total only exists inside calculate_total. Once the function finishes running, that variable is gone — trying to print it outside the function raises a NameError.

Global Scope

A variable created at the top level of a script — outside any function — belongs to the global scope. It can be read from anywhere in the file, including from inside functions.

global_scope.py
app_name = "PrograMinds"  # global variable

def show_app_name():
    print("Running:", app_name)

show_app_name()
print("Still accessible:", app_name)
Output
Running: PrograMinds
Still accessible: PrograMinds

Functions can freely read global variables. The tricky part, as you will see next, is reassigning a global variable from inside a function.

Local

Created inside a function; only visible inside that function.

Global

Created at the top level of a module; visible everywhere in that file.

Read vs. Write

Functions can read a global variable freely, but writing to it needs special handling.

The global Keyword

By default, assigning to a variable name inside a function creates a new local variable, even if a global variable with the same name already exists. The global keyword tells Python that a name inside the function refers to the global variable, so assignment actually updates it.

without_global.py
counter = 0

def increment():
    counter = counter + 1  # tries to use counter before assigning locally
    print(counter)

increment()
Output
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'counter' referenced before assignment

Because increment() assigns to counter, Python treats counter as local to that function for its entire body — so reading counter before that assignment fails.

with_global.py
counter = 0

def increment():
    global counter
    counter = counter + 1
    print("Inside function:", counter)

increment()
increment()
print("Outside function:", counter)
Output
Inside function: 1
Inside function: 2
Outside function: 2

The global counter line tells Python explicitly: "counter refers to the global variable, not a new local one." Now each call updates the same shared variable.

Enclosing Scope & nonlocal

When you define a function inside another function, the inner function can read variables from the outer function's scope — this is called the enclosing scope. Just like with global variables, assigning to that name inside the inner function creates a new local variable instead, unless you use nonlocal.

enclosing_scope.py
def outer():
    message = "Hello"

    def inner():
        print("Inner sees:", message)  # reading works fine

    inner()

outer()
Output
Inner sees: Hello
nonlocal_example.py
def make_counter():
    count = 0

    def increment():
        nonlocal count
        count += 1
        return count

    return increment

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

nonlocal tells Python that count inside increment refers to the count variable in the enclosing make_counter function, not a brand-new local variable. Each call to counter() updates and remembers that same enclosing count.

global vs. nonlocal
  • global reaches all the way out to the module-level (top-of-file) scope.
  • nonlocal reaches out to the nearest enclosing function scope — not the global scope.
  • Both keywords are only needed when you want to assign to (not just read) an outer variable.

The LEGB Rule

Whenever Python looks up a name, it searches through scopes in a specific order known as LEGB: Local, then Enclosing, then Global, then Built-in. It stops and uses the first match it finds.

1️⃣ Local (L)

Names assigned inside the current function.

2️⃣ Enclosing (E)

Names in any enclosing (outer) function, for nested functions.

3️⃣ Global (G)

Names assigned at the top level of the current module/file.

4️⃣ Built-in (B)

Names Python provides automatically, like print, len, and range.

legb_demo.py
value = "global value"

def outer():
    value = "enclosing value"

    def inner():
        value = "local value"
        print(value)  # Local wins

    inner()
    print(value)  # Enclosing wins here

outer()
print(value)  # Global wins here
print(len)  # Built-in name, found in the Built-in scope
Output
local value
enclosing value
global value
<built-in function len>

Each print() call resolves value using the closest scope available at that point: inner() finds its own local value first, outer() falls back to its own enclosing value, and the final print at module level finds the global value.

The Danger of Shadowing

Shadowing happens when a local variable uses the same name as a global (or enclosing) variable, hiding it inside that scope. This can cause confusing bugs where code appears to "lose track" of a value.

shadowing_bug.py
total = 1000  # global "total"

def apply_discount(price):
    total = price * 0.9  # this creates a NEW local "total", shadowing the global one
    return total

discounted = apply_discount(500)
print("Discounted price:", discounted)
print("Global total is unchanged:", total)
Output
Discounted price: 450.0
Global total is unchanged: 1000

Inside apply_discount, total is a brand-new local variable — it never touches the global total. This is usually fine here, but if the programmer expected the function to update the global total, this shadowing would be a real bug.

Why Shadowing Is Risky
  • It can make code confusing — the same name means two different things in two places.
  • A function may silently fail to update a global variable the way you expected.
  • Bugs caused by shadowing are often hard to spot because there is no error — just wrong behavior.

Common Mistakes

Avoid These Mistakes
  • Forgetting global (or nonlocal) and being surprised that a function did not update an outer variable.
  • Overusing global variables, which makes programs harder to reason about and test.
  • Assuming a variable created inside a function is accessible outside it — it is not.
  • Reassigning a name inside a function without realizing it shadows an outer variable of the same name.
  • Using global everywhere instead of simply returning a value from the function and reassigning it at the call site.

Best Practices

  • Prefer passing values in as parameters and getting results out via return over relying on global.
  • Reserve global and nonlocal for the specific cases where you truly need to modify an outer variable.
  • Choose distinct variable names to avoid accidentally shadowing an outer-scope variable.
  • Keep functions self-contained — minimize how much they depend on global state.
  • When you do use global, keep the list of global variables small and well understood.

Frequently Asked Questions

Can a function read a global variable without using the global keyword?

Yes. Reading a global variable works automatically. The global keyword is only needed when you want to assign a new value to that global variable from inside the function.

What is the difference between global and nonlocal?

global refers to the module-level (top-of-file) scope. nonlocal refers to the nearest enclosing function scope, used for nested functions — it never reaches all the way to the global scope.

What does LEGB stand for?

Local, Enclosing, Global, Built-in — the order Python searches through scopes when resolving a variable name, stopping at the first match.

Is it bad practice to use global variables?

Not always wrong, but overusing global state makes code harder to test and reason about. Passing values through parameters and return values is usually cleaner.

What happens if a local variable has the same name as a global one?

The local variable shadows the global one inside that function — code inside the function sees only the local version, and the global variable outside remains untouched unless you use global explicitly.

Key Takeaways

  • A local variable only exists inside the function where it was created.
  • A global variable is created at the top level of a file and can be read anywhere in it.
  • The global keyword lets a function assign to a global variable instead of creating a local one.
  • The nonlocal keyword lets a nested function assign to a variable in its enclosing function.
  • The LEGB rule (Local, Enclosing, Global, Built-in) determines how Python resolves a variable name.
  • Shadowing an outer-scope name with a local variable of the same name can cause confusing bugs.

Summary

Variable scope determines where a name can be seen and used in your code. Local variables live and die inside their function, global variables are visible throughout a file, and the global and nonlocal keywords let you deliberately reach outside the current scope to modify a variable.

The LEGB rule ties all of this together, explaining exactly how Python decides which variable a name refers to. Next, you will learn about lambda functions — a compact way to write small, throwaway functions.

Lesson 25 Completed
  • You understand the difference between local and global scope.
  • You can use global to modify a global variable from inside a function.
  • You understand enclosing scope and how nonlocal works in nested functions.
  • You know the LEGB rule Python uses to resolve variable names.
  • You are ready to learn about lambda functions.
Next Lesson →

Lambda Functions