Skip to main content

Python Scope

When you're working with variables in Python, one crucial concept to understand is "scope." Scope defines where in your program a variable can be accessed or modified. Mastering Python's scope rules will help you write better, bug-free code and understand how information flows through your programs.

What is Scope?

In Python, scope refers to the region of your code where a variable is visible and accessible. When you create a variable, it exists within a specific scope, which determines where you can use that variable.

Think of scope as the "visibility" of variables - some variables can be seen and accessed from anywhere in your code, while others are only visible in specific sections.

Python's LEGB Rule

Python follows the LEGB rule to determine the scope of variables. LEGB stands for:

  • Local
  • Enclosing
  • Global
  • Built-in

Let's explore each of these scopes in detail.

Local Scope

A variable created inside a function belongs to the local scope of that function and can only be used inside that function.

python
def my_function():
# Local variable
message = "Hello, local scope!"
print(message)

my_function() # Output: Hello, local scope!

# This would cause an error because message is not defined outside the function
# print(message) # NameError: name 'message' is not defined

Enclosing (or Nonlocal) Scope

This refers to variables in the outer function when you have nested functions.

python
def outer_function():
outer_var = "I'm in the outer function"

def inner_function():
print(outer_var) # Accessing variable from the enclosing scope

inner_function()

outer_function() # Output: I'm in the outer function

The inner function can access variables from the outer function, but not modify them directly (unless declared as nonlocal).

python
def outer_function():
count = 0

def inner_function():
nonlocal count # This allows modification of the enclosing scope variable
count += 1
print(f"Count inside inner function: {count}")

inner_function()
print(f"Count after calling inner function: {count}")

outer_function()
# Output:
# Count inside inner function: 1
# Count after calling inner function: 1

Global Scope

Variables created outside of any function have global scope, meaning they can be accessed from any part of the code.

python
# Global variable
global_var = "I'm a global variable"

def show_global():
print(global_var)

show_global() # Output: I'm a global variable
print(global_var) # Output: I'm a global variable

However, if you want to modify a global variable inside a function, you need to use the global keyword:

python
counter = 0

def increment_counter():
global counter # Declare that we want to modify the global variable
counter += 1
print(f"Counter inside function: {counter}")

print(f"Counter before: {counter}") # Output: Counter before: 0
increment_counter() # Output: Counter inside function: 1
print(f"Counter after: {counter}") # Output: Counter after: 1

Built-in Scope

Python comes with many built-in functions like print(), len(), and range(). These are always available in your code without needing to import them.

python
# Built-in functions are available everywhere
length = len("Python")
print(length) # Output: 6

Scope Resolution: How Python Looks for Variables

When you reference a variable in your code, Python follows the LEGB rule to find it:

  1. It first looks in the Local scope (the current function)
  2. Then it checks the Enclosing scope (any containing functions)
  3. Next, it searches the Global scope (module level)
  4. Finally, it checks the Built-in scope

Once it finds a matching variable name, it stops searching. If no match is found, Python raises a NameError.

Practical Examples

Example 1: Name Shadowing

When variables in different scopes have the same name, the local variable "shadows" the global one:

python
x = "global"

def demo_shadowing():
x = "local"
print(f"Inside function: x = {x}") # Uses the local x

demo_shadowing() # Output: Inside function: x = local
print(f"Outside function: x = {x}") # Uses the global x, Output: Outside function: x = global

Example 2: Creating a Counter Function

python
def create_counter():
count = 0

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

return increment

counter = create_counter()
print(counter()) # Output: 1
print(counter()) # Output: 2
print(counter()) # Output: 3

# Create another independent counter
counter2 = create_counter()
print(counter2()) # Output: 1
print(counter()) # Output: 4 (the first counter continues from where it left off)

This is a practical example of a closure, where the inner function remembers the environment in which it was created.

Example 3: Module-Level Scope

In a real application, you'll often work with multiple files. Each Python file (module) has its own global scope.

Imagine you have two files:

config.py:

python
# Global to this module
app_name = "My Application"
version = "1.0.0"

def get_app_info():
return f"{app_name} v{version}"

main.py:

python
import config

# Access variables from the imported module
print(config.app_name) # Output: My Application
print(config.get_app_info()) # Output: My Application v1.0.0

# This creates a variable in the global scope of main.py
app_name = "Different App"
print(app_name) # Output: Different App

# The original is unchanged
print(config.app_name) # Output: My Application

Common Pitfalls and Best Practices

Pitfall: Forgetting to Use global or nonlocal

python
count = 0

def increment_bad():
# This creates a new local variable instead of modifying the global one
count = count + 1 # UnboundLocalError: local variable 'count' referenced before assignment

# The correct way:
def increment_good():
global count
count = count + 1

Best Practice: Minimize Global Variables

Global variables can make code harder to understand and debug. It's generally better to pass variables as arguments:

python
# Not recommended
total = 0

def add_to_total(value):
global total
total += value

# Better approach
def add(a, b):
return a + b

result = 0
result = add(result, 5)
result = add(result, 10)

Best Practice: Use Function Parameters and Return Values

python
# Less clear, using global
message = ""

def set_greeting(name):
global message
message = f"Hello, {name}!"

set_greeting("Alice")
print(message) # Output: Hello, Alice!

# More clear, using parameters and return values
def create_greeting(name):
return f"Hello, {name}!"

greeting = create_greeting("Bob")
print(greeting) # Output: Hello, Bob!

Summary

Python's scope rules determine where variables are accessible in your code:

  • Local scope: Variables defined within a function
  • Enclosing scope: Variables from outer functions
  • Global scope: Variables defined at the module level
  • Built-in scope: Python's pre-defined functions and variables

Understanding scope helps you:

  • Prevent variable naming conflicts
  • Control which parts of your program can access data
  • Write more maintainable code by limiting variable visibility

Remember the LEGB rule for how Python searches for variables, and use the global and nonlocal keywords when you need to modify variables from outer scopes.

Exercises

  1. Write a function that increments a global counter and returns its new value.
  2. Create a nested function that can access and modify a variable from its parent function.
  3. Write a program with a local variable that shadows a global variable, and demonstrate the difference.
  4. Create a function that generates a series of functions, each remembering a different value.
  5. Rewrite a program that uses global variables to instead use function parameters and return values.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)