No history yet

Understanding Variable Scope

Where Do Variables Live?

Think of your code as a big house. In this house, there are different rooms. A variable is like an object you place in one of those rooms. If you're in the kitchen, you can easily use the toaster on the counter. But if you're upstairs in the bedroom, you can't just reach down and make toast. You have to be in the right room to use the object.

In Python, this concept is called scope. A variable's scope is the part of the program where it can be seen and used. Understanding scope helps you avoid naming conflicts and write code that behaves the way you expect.

The LEGB Rule

When you use a variable, Python has a specific order it follows to find it. It checks different “rooms” or scopes, one by one. This order is known as the LEGB rule:

  1. Local
  2. Enclosing
  3. Global
  4. Built-in

Python searches for a variable starting with the local scope. If it doesn't find it, it moves to the enclosing scope, then global, and finally the built-in scope. Let's break down what each of these means.

Local Scope

A variable created inside a function has a local scope. It's born inside that function, and it lives its whole life there. Once the function finishes running, the variable disappears.

def calculate_price():
    # tax_rate is a local variable
    tax_rate = 0.07
    total = 100 * (1 + tax_rate)
    print(f"Inside the function, the total is: {total}")

calculate_price()

# This will cause an error!
print(tax_rate)

If you run this code, the call to calculate_price() works perfectly. But the final print(tax_rate) line will crash your program with a NameError. Python looks for tax_rate outside the function but can't find it. Its scope is purely local to calculate_price().

Enclosing, Global, and Built-in Scopes

Things get more interesting when we have functions inside other functions, or variables defined outside all functions.

Lesson image

Enclosing Scope This applies when you have a nested function (a function inside another function). The inner function can access variables from the outer (enclosing) function.

def outer_function():
    # greeting is in the enclosing scope for inner_function
    greeting = "Hello"

    def inner_function():
        # inner_function can access the 'greeting' variable
        print(f"{greeting}, world!")

    inner_function()

outer_function()

Here, inner_function doesn't have its own greeting variable. Following the LEGB rule, Python first checks the local scope of inner_function (it's not there), then checks the enclosing scope of outer_function and finds it.

Global Scope Any variable you define at the top level of your script is in the global scope. It's accessible from anywhere in your file, including inside functions.

# PI is a global variable
PI = 3.14159

def calculate_circumference(radius):
    # This function can read the global variable PI
    circumference = 2 * PI * radius
    print(f"Circumference is: {circumference}")

calculate_circumference(10)

Be careful when modifying global variables from inside a function. If you just assign a new value, you're actually creating a new local variable with the same name, which 'hides' the global one. To truly modify a global variable, you need to use the global keyword.

Built-in Scope Finally, the built-in scope contains all the names that come with Python itself, like the print() function, the len() function for checking length, and types like str and int. These are always available everywhere, without you needing to define them.

By understanding this search order, you can predict exactly which variable your code will use and avoid bugs where one variable unintentionally hides another.

Time to check your understanding of variable scopes.

Quiz Questions 1/5

When Python searches for a variable, what is the correct order of scopes it checks, according to the LEGB rule?

Quiz Questions 2/5

What is the output of the following Python code?

def my_function():
    message = "Hello from inside"
    print(message)

my_function()
print(message)

Mastering scope is a key step toward writing clean, robust Python code.