Scope: Local, Enclosing, Global, and Built-in (LEGB Rule)
We've been working with variables and functions, but we haven't yet discussed a crucial concept: scope. Scope determines the visibility and accessibility of a variable. In other words, it answers the question, "Where in my program can I access this variable?" Understanding scope is essential for writing bug-free code and avoiding common errors where variables seem to "disappear" or have unexpected values.
Python has a set of rules for how it looks up a variable name, known as the LEGB rule.
📚 Prerequisites
Before diving into scope, you should be comfortable with:
- Defining and calling Python functions.
- Assigning values to variables.
🎯 Article Outline: What You'll Master
In this article, you will learn:
- ✅ What is Scope?: A clear definition of variable scope.
- ✅ The LEGB Rule: An acronym for the four scopes Python checks: Local, Enclosing, Global, and Built-in.
- ✅ Local Scope: Variables created inside a function.
- ✅ Global Scope: Variables created at the top level of a script.
- ✅ Enclosing Scope: The "in-between" scope that exists for nested functions.
- ✅ Built-in Scope: The special scope that holds all of Python's built-in functions.
- ✅ The
globalandnonlocalKeywords: How to modify variables in outer scopes.
🧠 Section 1: The LEGB Rule Explained
When you use a variable in your code, the Python interpreter needs to find where that variable was defined. It does this by searching through a sequence of scopes in a specific order. This order is defined by the LEGB rule:
- L - Local: The innermost scope. It's searched first. This contains names defined inside the current function.
- E - Enclosing: The scope of any enclosing functions. This is searched if the name isn't found in the local scope.
- G - Global: The top-level scope of the module/script. This is searched if the name isn't in the local or enclosing scopes.
- B - Built-in: The outermost scope. It contains all the names that are pre-loaded into Python, like
print(),len(), andstr(). This is the last place Python looks.
If the interpreter can't find the name in any of these scopes, it raises a NameError.
Let's break down each scope.
💻 Section 2: The Four Scopes
2.1 - Local (L) Scope
Any variable you define inside a function exists only in that function's local scope. This includes the function's parameters. These variables are created when the function is called and destroyed when the function finishes.
# local_scope_example.py
def my_function():
# 'x' is local to my_function
x = 10
print(f"Inside the function, x is: {x}")
my_function()
# This line would cause a NameError because 'x' does not exist outside the function.
# print(f"Outside the function, x is: {x}")
2.2 - Global (G) Scope
A variable defined at the top level of your script (i.e., not inside any function) is in the global scope. It can be accessed from anywhere in your script, including inside functions.
# global_scope_example.py
# 'y' is a global variable
y = 100
def another_function():
# The function can READ the global variable 'y'
print(f"Inside the function, y is: {y}")
another_function()
print(f"Outside the function, y is: {y}")
# Output:
# Inside the function, y is: 100
# Outside the function, y is: 100
2.3 - Enclosing (E) Scope
This scope only exists for nested functions. If you define a function inside another function, the inner function can access variables from the outer (enclosing) function.
# enclosing_scope_example.py
def outer_function():
# 'z' is in the enclosing scope for inner_function
z = 200
def inner_function():
# inner_function can access 'z' from its enclosing scope
print(f"Inside the inner function, z is: {z}")
inner_function() # Call the inner function
outer_function()
2.4 - Built-in (B) Scope
This scope contains all the names that are built into Python. You've been using them all along! Functions like print(), len(), type(), and exceptions like ValueError all live in this scope.
You can think of it as a universal scope that's always available.
🛠️ Section 3: Modifying Variables in Outer Scopes
So far, we've only been reading variables from outer scopes. What if you want to change them? By default, if you assign a value to a variable inside a function, Python assumes you are creating a new local variable.
x = 10 # Global variable
def attempt_to_modify_global():
x = 50 # This creates a NEW local variable 'x'
print(f"Inside function: {x}")
attempt_to_modify_global()
print(f"Outside function: {x}") # The global 'x' is unchanged
# Output:
# Inside function: 50
# Outside function: 10
To explicitly tell Python you want to modify a variable in an outer scope, you use the global and nonlocal keywords.
globalkeyword: Declares that a variable inside a function is the one from the global scope.nonlocalkeyword: Declares that a variable inside a nested function is the one from the enclosing scope.
# global_and_nonlocal_example.py
# --- Global Example ---
count = 0 # Global variable
def increment_global_counter():
global count # Declare our intent to modify the global 'count'
count += 1
print(f"Global count is now: {count}")
increment_global_counter()
increment_global_counter()
# --- Nonlocal Example ---
def create_counter():
# 'num' is in the enclosing scope for increment_enclosing
num = 0
def increment_enclosing():
nonlocal num # Declare our intent to modify the enclosing 'num'
num += 1
return num
return increment_enclosing
counter_func = create_counter()
print(f"Enclosing count is: {counter_func()}")
print(f"Enclosing count is: {counter_func()}")
✨ Conclusion & Key Takeaways
Understanding the LEGB rule is a major step toward mastering Python. It demystifies how Python finds variables and helps you avoid subtle bugs related to variable naming and modification.
Let's summarize the key takeaways:
- LEGB is the Order of Search: Python looks for variables in this order: Local -> Enclosing -> Global -> Built-in.
- Local by Default: Variables created in a function are local to it unless specified otherwise.
- Reading is Easy, Writing is Explicit: Functions can read from outer scopes freely, but you must use
globalornonlocalto write to them. globalfor Global Scope: Modifies a variable at the top level of the script.nonlocalfor Enclosing Scope: Modifies a variable in the immediate outer function (for nested functions).
Challenge Yourself: Predict the output of the following code snippet before running it. This will test your understanding of the LE_B rule (no Enclosing scope here).
value = "global"
def my_func():
# print(value) # What would happen if you uncommented this line?
value = "local"
print(value)
my_func()
print(value)
➡️ Next Steps
Now that you understand how functions handle variables and scope, the next step is to learn how to document them properly. In our next article, we will cover "Docstrings and Type Hinting," two essential tools for writing clean, maintainable, and professional Python code.
Happy coding!
Glossary (Python Terms)
- Scope: The region of a program where a variable is accessible.
- Namespace: A mapping from names to objects. Scopes are implemented as namespaces.
- LEGB Rule: The sequence of scopes (Local, Enclosing, Global, Built-in) that Python searches to resolve a name.
global: A keyword that declares a variable as belonging to the global scope.nonlocal: A keyword that declares a variable as belonging to the enclosing scope.