Python Functions Scope
Where Variables Live: Understanding Scope
In Python, not all variables are accessible from every part of your program. The scope of a variable determines the region of your code where that variable is recognized and can be used.
Understanding variable scope is fundamental to writing correct, predictable, and bug-free Python code. It helps you manage variable names and prevents unintended interactions between different parts of your program, especially when working with functions.
What is Variable Scope?
Scope refers to the context in which a variable is defined. It defines the variable's lifetime and visibility.
Python follows the LEGB rule to determine the scope of a variable. When you try to access a variable, Python searches for it in this order:
- Local (L): Inside the current function or class method.
- Enclosing (E): In the scope of any enclosing functions (for nested functions).
- Global (G): At the top level of the current module (outside any function or class).
- Built-in (B): In the special built-in names provided by Python (like
print
,len
,True
,False
,None
).
If Python doesn't find the variable in any of these scopes, it raises a NameError
.
Local Scope
Variables defined inside a function have local scope. They are created when the function is called and cease to exist when the function finishes executing.
You can access a local variable only from within the function where it is defined.
def my_function():
# 'x' has local scope
x = 10
print(f"Inside the function, x is: {x}")
my_function()
# Trying to access x outside the function will cause an error
# print(x) # This line would cause a NameError
Output:
Inside the function, x is: 10
# If you uncomment the last line, you'll get:
# NameError: name 'x' is not defined
The local variable x
is only visible and usable within my_function()
.
Global Scope
Variables defined at the top level of a script, outside of any function or class, have global scope. Global variables can be accessed (read) from anywhere in the script, including inside functions.
# 'y' has global scope
y = 20
def another_function():
# You can read the global variable y from inside the function
print(f"Inside the function, y is: {y}")
another_function()
print(f"Outside the function, y is: {y}")
Output:
Inside the function, y is: 20
Outside the function, y is: 20
However, by default, you cannot modify a global variable from inside a function. If you try to assign a value to a variable name that is defined in the global scope, Python creates a new local variable with the same name inside the function instead.
This can lead to confusion or the UnboundLocalError
if you try to read the variable before assigning to it within the function:
z = 30 # Global variable
def modify_global_attempt():
# Python thinks z here is a new local variable.
# Trying to use it before assigning to it causes an error.
z = z + 1 # ERROR: UnboundLocalError!
print(f"Inside function: {z}")
# modify_global_attempt() # This call would cause the UnboundLocalError
print(f"Outside function, z is still: {z}") # z remains 30 globally
The global
Keyword
To modify a global variable from inside a function, you must explicitly tell Python your intention using the global
keyword before the variable name.
count = 0 # Global variable
def increment_global():
global count # Declare that we intend to modify the global 'count'
count = count + 1 # Now this modifies the global variable
print(f"Inside function, count is: {count}")
print(f"Outside function before call, count is: {count}") # Output: 0
increment_global()
print(f"Outside function after call, count is: {count}") # Output: 1
Using global
should be done cautiously, as it can make code harder to track and debug by allowing functions to have side effects on the global state.
Enclosing (Nonlocal) Scope
Python allows you to define functions inside other functions. These are called nested functions. The inner function has access to variables in its own local scope, the global scope, the built-in scope, and importantly, the scope of the outer (enclosing) function(s).
This intermediate scope is called the enclosing scope.
def outer_function():
outer_variable = "I'm in the outer scope"
def inner_function():
# inner_function can access outer_variable (enclosing scope)
print(f"Inside inner function: {outer_variable}")
inner_function() # Call the inner function
outer_function()
# You cannot access outer_variable from outside outer_function
# print(outer_variable) # This would cause a NameError
Output:
Inside inner function: I'm in the outer scope
The inner_function
can see outer_variable
from its enclosing scope.
Similar to global variables, by default, an inner function cannot modify variables in its enclosing scope. An assignment inside the inner function will create a new local variable there.
The nonlocal
Keyword
To modify a variable in the nearest enclosing scope (that is not the global scope) from within a nested function, you use the nonlocal
keyword.
def counter():
count = 0 # Variable in the enclosing scope
def increment():
nonlocal count # Declare that we intend to modify the 'count' in the enclosing scope
count += 1
print(f"Inner function: count is {count}")
increment() # Call inner to modify count
increment() # Call inner again
print(f"Outer function: final count is {count}")
counter()
Output:
Inner function: count is 1
Inner function: count is 2
Outer function: final count is 2
Without nonlocal
, the count += 1
inside increment()
would try to create a new local count
or cause an UnboundLocalError
. nonlocal
targets the count
variable in counter()
's scope.
Summary
- Scope determines where variables are accessible.
- Python follows the LEGB rule to look for names: Local, Enclosing, Global, Built-in.
- Local variables are defined inside a function and only exist there.
- Global variables are defined at the top level and can be read from inside functions.
- Use the
global
keyword to modify a global variable from inside a function. - Enclosing scope refers to variables in an outer function when using nested functions.
- Use the
nonlocal
keyword to modify a variable in the nearest enclosing scope.
Understanding scope is crucial for managing variable lifetimes and visibility, preventing naming conflicts, and writing correct and maintainable Python programs.