Python will raise a NameError
, indicating that the variable is not defined in any scope (Local, Enclosing, Global, or Built-in).
Short answer:
When we use a variable name, how does Python know where to find its value? It uses the LEGB rule!
LEGB stands for Local, Enclosing, Global, Built-in. These are the possible scopes of a variable in Python. When we use a variable name in Python, it follows this sequence to find its value.
Have you ever tried accessing a variable in Python and encountered a NameError
, even though you're sure you defined it somewhere? Or maybe you’ve accidentally modified a global variable inside a function, leading to unexpected bugs? These challenges often arise from confusion about variable scope.
To resolve such issues, Python uses the LEGB rule to determine where a variable's value comes from. The LEGB rule outlines the order in which Python searches for variables, helping us understand and control how our code accesses or modifies them.
In Python, variables are stored in namespaces, which act as containers for mapping names to objects. When Python encounters a variable, it searches through different namespaces to find its value, following the LEGB rule.
L - Local:: Variables defined within the current function.
E - Enclosing: Variables in the local scope of any enclosing functions (for nested functions).
G - Global: Variables defined at the module level (outside any function or class).
B - Built-in: Names pre-defined in Python, such as len()
or dict
.
Python searches in the order Local → Enclosing → Global → Built-in until it finds the variable. If the variable isn't found, it raises a NameError
.
A namespace is essentially a container that holds variable names and their corresponding objects. Think of it as a mapping between names and objects. In Python, every time a function or a block of code is executed, a new namespace is created. When the block finishes, the namespace is destroyed.
The following code snippet will create a namespace that contains a variable with name x
and value 3
. After the program finishes running, this namespace is deleted.
x = 3
Let's look at a diagram that represents a namespace. The namespace is drawn as a big box containing variable names with arrows pointing to objects (boxes). Inside each box is a value(s). An arrow from a variable name to a box indicates that the name maps to the object that the box represents.
The code snippet above could be represented in a diagram like this:
Namespaces can also be nested. Calling a function creates a new namespace:
def f(x):return xy = f(3)
Look at the illustration below to see the namespaces and the objects they contain in the example above. Notice that function parameters are stored in that function's namespace when it is created (i.e., when the function is called).
The namespace that contains a variable determines when that variable is in scope, that is, when it can be used.
Now, let's break down each scope with examples.
x = 5 # Global variabledef f(x):print(f"The value of x is {x}") # Local scopef(3)
In this example, x = 5
exists in the global namespace, and when we call the function f(3)
, a new local scope is created inside the function. The argument 3
gets assigned to the local x
, overriding the global x
inside the function. Thus, Python prints "The value of x is 3
" because it checks the local scope first.
Try it yourself:
Try changing the argument passed to the function: What happens if you pass
f(7)
instead off(3)
? Predict the output before running the code.Modify the global variable
x
: What happens if you changex = 5
tox = 10
outside the function and then callf()
again with different values?Remove the parameter in the function definition: What will happen if you modify the function
f()
to remove the parameterx
entirely? How will the globalx
behave in this case?
x = 5 # Global scopedef outer():x = 10 # Enclosing scopedef inner():print(f"The value of x is {x}") # Refers to enclosing scopeinner()outer()
In this example, x = 10
is in the enclosing scope of outer()
, and the inner()
function has access to it. When we call inner()
, Python looks for x
in the local scope first (but doesn't find it) and then checks the enclosing scope, printing "The value of x is 10"
.
Try yourself:
Try changing the value of
x
in the enclosing scope: What will happen if you modify the value ofx
insideouter()
(for example, setx = 15
)? How does it affect the output ofinner()
? In this case, the variablex
inouter()
is in the enclosing scope, which meansinner()
can access it.Modify the function call sequence: What happens if you call
outer()
more than once or change the nesting of the functions? How does the scope resolution change in these scenarios?Add a global variable: Try adding
x = 20
in the global scope and see how Python resolvesx
when you add another print statement after callinginner()
.
NameError
in the absence of a variableLook at the example below:
def f():print(x)f() # Raises NameError
Since x
is not defined in the local, enclosing, global, or built-in scopes, Python raises a NameError
.
This scope contains all built-in functions and variables like len()
, dict()
, and print()
.
print(len("hello")) # 'len' is a built-in function
Python follows the LEGB rule, searching for variables in the Local, Enclosing, Global, and Built-in scopes in that order.
In Python, understanding namespaces and scope-related keywords like globals()
, locals()
, global
, and nonlocal
can help with debugging and modifying variables across different scopes.
Sometimes you might want to inspect all the current variables in a given namespace. Python offers built-in functions for this:
globals()
: Returns a dictionary of all the global variables.
locals()
: Returns a dictionary of local variables within the current function or scope.
These are useful tools for debugging or gaining visibility into what objects are currently in a namespace. Additionally, globals()
can be used to dynamically access or modify global variables, though this should be done cautiously to avoid unintended side effects.
global
and nonlocal
There are cases where you need to access or modify variables in different namespaces explicitly, which can be done with the global
and nonlocal
keywords.
For example, suppose you want to modify a global variable inside a function:
x = 5def f():# This new value of `x` will persist only# while the namespace of f still existsx = 3print(f'Before: the value of x is {x}')f()print(f'After: the value of x is {x}')
We can avoid this by using the keyword global
before the variable name. Declaring global x
will let us use the name x
as if we were in the global namespace.
x = 5def f():# Declaring x as global allows us to modify# its value in the global namespaceglobal xx = 3print(f'Before: the value of x is {x}')f()print(f'After: the value of x is {x}')
To access a variable in the closest enclosing namespace, we can use the keyword nonlocal
. For example:
def outer():x = 5def inner():nonlocal xx = 3 # modifies `x` in the enclosing `outer` functioninner()print(x) # prints 3outer()
In this case, nonlocal
allows us to modify x
in the outer
function’s scope, not in the global scope.
However, using these keywords is generally considered bad practice. It is confusing to read code that uses objects outside of a modularized portion.
The following actions create new scopes in Python:
The following actions do not create new scopes:
Entering an if
statement: Variable assignments within an if
block affect the current scope.
Entering a for or while loop: Variables defined within loops also stay in the current scope.
Creating a context manager (using with statements): These do not create a new scope either.
Test your understanding of the LEGB rule
What will the following code print?
x = 50 # Global scope
def outer():
x = 30 # Enclosing scope
def inner():
x = 10 # Local scope
print(x)
inner()
outer()
50
30
10
NameError
Modify the following code so that the outer()
function changes the global value of x
to 100
while keeping inner()
's x
value as 50
.
x = 10 # Global scopedef outer():x = 50 # Enclosing scopedef inner():print(f"Inner x: {x}")inner()outer()print(f"Global x: {x}")
Key takeaways
LEGB rule: Python follows a specific order for resolving variable names—Local, Enclosing, Global, and Built-in—known as the LEGB rule.
Scope creation: Scopes are defined within functions, classes, and modules, and they help isolate variables in different contexts.
Name isolation: When variables with the same name exist in different scopes, their values are isolated and only accessible within their specific scope.
Modifying variables: Changing a variable in one scope does not affect the same-named variable in another scope.
global
and nonlocal
keywords: While generally discouraged, these keywords can be used to modify variables in higher scopes.
Become a Python Developer with Our Comprehensive Learning Path!
Ready to kickstart your career as a Python Developer? Our Become a Python Developer path is designed to take you from your first line of code to landing your first job.
This comprehensive journey offers essential knowledge, hands-on practice, interview preparation, and a mock interview, ensuring you gain practical, real-world coding skills. With our AI mentor by your side, you’ll overcome challenges with personalized support, building the confidence needed to excel in the tech industry.
Haven’t found what you were looking for? Contact Us