Mastering Python Scope: A Step-by-Step Guide to the LEGB Rule
Introduction
Understanding Python scope is essential for writing clean, predictable code. The LEGB rule—Local, Enclosing, Global, Built-in—defines how Python resolves variable names. This guide will walk you through the hierarchy step by step, show you how to inspect and modify scopes, and help you avoid common pitfalls. By the end, you'll be able to confidently navigate scope boundaries in your Python programs.

What You Need
- A Python interpreter (version 3.x recommended)
- Basic understanding of functions and variable assignment
- Willingness to experiment with code examples
Step-by-Step Instructions
Step 1: Grasp the Concept of Scope
Scope determines where a variable name is accessible. Python uses lexical (static) scoping: the scope is determined at compile time based on the structure of the code. Start by creating a simple variable at the top level of a script and try to access it inside a function. Note that variables defined outside functions are global by default.
Step 2: Learn the LEGB Order
When Python looks up a name, it searches in this order:
- Local – inside the current function
- Enclosing – any and all enclosing functions (from innermost outward)
- Global – the top-level scope of the module
- Built-in – names in the builtins module (like
print,len)
The search stops at the first match. If nothing is found, a NameError is raised.
Step 3: Identify Local Scope
Any variable assigned inside a function (including parameters) exists in that function's local scope. Write a function that defines a variable and returns it. Try accessing the same variable outside the function—it will raise a NameError because it's local.
def my_func():
x = 10 # local to my_func
return x
print(x) # NameError: name 'x' is not defined
Step 4: Understand Enclosing Scope (Nested Functions)
When you have a function inside another function, the inner function can access variables from the enclosing function's scope (but not the other way around). This is the E in LEGB. Create an outer function with a variable, then an inner function that uses that variable. Notice that the inner function can read the variable without any special declaration.
def outer():
y = 20
def inner():
print(y) # accesses y from enclosing scope
inner()
outer() # prints 20
Step 5: Work with Global Scope
Variables defined at the module level are global. They can be read inside functions without any special keyword. But if you try to assign to a global variable inside a function, Python creates a new local variable unless you use the global statement. To modify a global variable from within a function, declare it with global.
z = 100
def modify_global():
global z
z = 200
modify_global()
print(z) # 200, because global was used
Step 6: Explore Built-in Scope
Built-in names are always available. Examples: print, len, range. You can shadow a built-in by naming one of your variables the same, but it's not recommended. To see the built-in scope, you can inspect dir(__builtins__). Never override built-ins unless you have a very good reason.
Step 7: Use the global Statement
The global statement tells Python that a variable name should be treated as referring to the global scope, even if assigned inside a function. Use it sparingly—excessive global variables can make code hard to debug. Only use global when you truly need to modify a module-level variable from within a function.

Step 8: Use the nonlocal Statement
The nonlocal statement works similarly but for enclosing (non-global) scopes. It allows you to assign to a variable in an enclosing function scope from inside a nested function. Without nonlocal, assignment in the inner function creates a new local variable instead.
def outer():
a = 1
def inner():
nonlocal a
a = 2
inner()
print(a) # 2, because nonlocal allowed modification
outer()
Step 9: Test Your Knowledge with Examples
Now practice by writing small programs that mix different scopes. Try to predict the output of code snippets. For example:
def func1():
x = 5
def func2():
x = 10
print(x)
func2()
print(x)
func1() # What will be printed?
Answer: 10 then 5 – because x in func2 is local, not the same as x in func1.
Tips and Common Pitfalls
- Shadowing built-ins: Avoid naming variables
list,dict,str, etc. Use slightly different names instead. - Unintended globals: If you forget
globaland assign a variable inside a function, Python treats it as a new local. This can cause bugs when you think you're updating a global. - Closures: When you return a nested function that references enclosing variables, the variable values are preserved in a closure. This is powerful but can lead to memory leaks if not managed.
- Debugging scope issues: Use
print(locals()),print(globals())or a debugger to inspect which variables are in scope at any point. - Readability: Limit the depth of nested functions. Deep nesting makes the LEGB rule harder to follow.
- Practice with quizzes: After learning, challenge yourself with a short quiz to solidify the concepts. Many online resources offer exercises on Python scope.
Remember, mastering scope takes practice. Start with simple examples and gradually introduce nested functions, global modifications, and closures. The LEGB rule is your roadmap—use it to navigate variable resolution with confidence.
Related Articles
- Beyond Pixels: How Information Theory Transforms Imaging System Design
- Building Trust in a World of Information Overload: A Leader's Guide
- 10 Key Insights Into Cloudflare's Autonomous AI Agent Deployment
- 10 Essential Steps to Build a Natural Language Interface for Spotify Ads with Claude Code Plugins
- 10 Things You Need to Know About Cloudflare Giving AI Agents the Keys to the Cloud
- Microsoft's CTO Reveals Windows 11 Built on Decades-Old Code: A 'Bedrock' from the 1990s
- AI Coding Boom Obscures Crisis: Junior Developers Losing Ability to Debug Their Own Code
- Go 1.26: Key Features and Changes Explained