What is the nonlocal Keyword in Python?
The nonlocal keyword is used to declare variables in a nested function scope, allowing you to modify the value of variables in the enclosing (non-global) scope. It works similarly to the global keyword, but instead of referring to global variables, it targets variables in the nearest enclosing function scope.
By using nonlocal, you can avoid creating new local variables in a nested function and instead update the variable defined in the outer function. This is helpful when you want to modify the value of variables within a function without changing them globally.
Advantages of nonlocal
- Access to Outer Scope Variables: The nonlocal keyword enables a function to modify variables from an enclosing function that aren't in the global scope. This is useful for maintaining state across multiple function calls without using global variables.
- Improved Code Readability: By using nonlocal, you avoid the need to pass variables explicitly between functions, reducing the number of arguments passed and making your code cleaner and easier to read.
- No Need for Global Variables: When you want to modify a variable in a nested function, using nonlocal is more efficient than relying on global variables, which can be harder to manage and lead to bugs in larger programs.
- Control Over Variable Scope: The nonlocal keyword helps you control the scope of variables, allowing you to maintain a more predictable flow in your program by making sure certain variables stay confined to a specific scope, rather than affecting the entire program.
Disadvantages of nonlocal
- Can Lead to Confusion: While nonlocal can make your code more concise, it can also make it harder to understand, especially for beginner programmers. It can be difficult to track changes to variables when they are modified in different scopes.
- Limited to Nested Functions: The nonlocal keyword can only be used in nested functions, meaning it won't help if you want to modify a variable from a global or non-enclosing scope. This limitation means that its usage is more specific and doesn't apply universally.
- Not Ideal for Large Programs: In large programs, overuse of nonlocal can lead to tangled code where tracking changes to variables becomes difficult. It's generally better to use classes or other approaches for managing state in larger applications.
Nonlocal in Nested Functions
When you define a function inside another function in Python, it's called a nested function. Nested functions can be very useful for organizing code & creating helper functions that are only needed within a specific context. However, working with variables in nested functions can be tricky.
Let's say you have an outer function that defines a variable, & an inner function that wants to modify that variable. By default, if you try to assign a new value to the variable inside the inner function, Python will create a new local variable with the same name rather than modifying the outer variable.
For example:
def outer_func():
x = 10
def inner_func():
x = 20
print(f"Inner x: {x}")
inner_func()
print(f"Outer x: {x}")
outer_func()

You can also try this code with Online Python Compiler
Run Code
Output:
Inner x: 20
Outer x: 10
Understanding Variable Scope in Python
Before understanding the nonlocal keyword in detail, it's important to have a solid understanding of variable scope in Python. Variable scope refers to the region of a program where a variable is visible & accessible.
In Python, there are two main types of variable scope: local & global.
Local Scope
- Variables defined inside a function are in the local scope of that function.
- Local variables can only be accessed within the function where they're defined.
- When the function finishes executing, the local variables are destroyed.
Example:
def my_func():
x = 10 # Local variable
print(x)
my_func()
print(x) # Error: x is not defined

You can also try this code with Online Python Compiler
Run Code
Output
10
Global Scope:
- Variables defined outside any function are in the global scope.
- Global variables can be accessed from anywhere in the program, including inside functions.
- They persist throughout the entire execution of the script.
Example:
x = 10 # Global variable
def my_func():
print(x)
my_func()
print(x)

You can also try this code with Online Python Compiler
Run Code
Output
10
10
In addition to local & global scopes, Python also has a special scope called the nonlocal scope. This comes into play when you have nested functions.
Nonlocal Scope
- Refers to variables defined in an enclosing function scope, but not in the global scope.
- Allows nested functions to access & modify variables from outer functions.
- Uses the nonlocal keyword to indicate that a variable is from an enclosing scope.
Example:
def outer_func():
x = 10 # Nonlocal variable
def inner_func():
nonlocal x
x = 20 # Modifies the nonlocal variable
inner_func()
print(x) # Output: 20
outer_func()

You can also try this code with Online Python Compiler
Run Code
Output
20
Understanding these different scopes is crucial for working with variables effectively in Python, especially when dealing with nested functions & the nonlocal keyword.
Expanding the Scope: Python Nonlocal in Larger Scripts
While the examples we've seen so far have been relatively small, the nonlocal keyword can be particularly useful when working with larger Python scripts that involve multiple levels of nested functions.
Imagine you're building a complex data processing pipeline that involves several stages of transformation & analysis. You might have a main function that sets up the pipeline, and then a series of nested functions that handle each stage of the process.
For example:
def process_data(data):
results = []
def stage1(data):
# Perform stage 1 processing
processed_data = [item.upper() for item in data]
return processed_data
def stage2(data):
# Perform stage 2 processing
filtered_data = [item for item in data if len(item) > 5]
return filtered_data
def stage3(data):
nonlocal results
# Perform stage 3 processing
results = [item[::-1] for item in data]
# Run the pipeline
data = stage1(data)
data = stage2(data)
stage3(data)
return results
# Example usage
data = ['apple', 'banana', 'cherry', 'date']
final_results = process_data(data)
print(final_results)

You can also try this code with Online Python Compiler
Run Code
Output:
['ANANAB', 'YRREHC']
In this example, we have a process_data() function that sets up our data processing pipeline. Inside it, we define three nested functions: stage1(), stage2(), and stage3(), each handling a different stage of the process.
The stage3() function uses the nonlocal keyword to modify the results variable defined in the outer process_data() function. This allows us to accumulate the final results of our pipeline in a single variable that's accessible from the outer scope.
With nonlocal, we can avoid passing the results variable through each stage of the pipeline explicitly, which can make the code cleaner & more manageable to read.
Of course, this is just a simplified example, but the same principles apply to more complex scripts. When multiple levels of nested functions need to share and modify state, the nonlocal keyword can be a powerful tool for managing variable scope and keeping your code organized.
Note: Just remember to use nonlocal with caution and only when it is really required for your program's structure. Overusing nonlocal can make your code harder to understand & maintain, so always try to use alternative approaches like function arguments & return values when appropriate.
Examples
Let’s now look at a few examples to better understand the practical use of the nonlocal keyword.
Example 1: Modifying Outer Variable in a Nested Function
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
count_fn = counter()
print(count_fn())
print(count_fn())

You can also try this code with Online Python Compiler
Run Code
Output
1
2
Explanation: Here, the counter function defines a variable count. The nested increment function modifies the count variable using the nonlocal keyword. Each time we call count_fn(), the value of count is incremented by 1.
Example 2: Using nonlocal to Track State Across Function Calls
def make_multiplier(factor):
result = 0
def multiplier(number):
nonlocal result
result = number * factor
return result
return multiplier
times_two = make_multiplier(2)
print(times_two(5)
print(times_two(7))

You can also try this code with Online Python Compiler
Run Code
Output
10
14
Explanation: In this example, the make_multiplier function returns a nested function that multiplies a number by a given factor. The nonlocal keyword is used to modify the result variable, which tracks the outcome of each multiplication.
As you can see, assigning x = 20 inside inner_func() doesn't change the value of x in outer_func(). That's where the nonlocal keyword comes in.
Exploring Alternatives to Python Nonlocal
While the nonlocal keyword is a powerful tool for modifying variables in outer scopes, it's not the only way to achieve similar results. In some cases, you should look for alternative approaches that can make your code clearer or more efficient.
One common alternative is to use mutable objects like lists or dictionaries to store and update values. Instead of trying to modify a simple variable from an inner function, you can define a mutable object in the outer function and modify its contents in the inner function.
Let’s take an example using a list:
def outer_func():
x = [10]
def inner_func():
x[0] = 20
print(f"Inner x: {x[0]}")
inner_func()
print(f"Outer x: {x[0]}")
outer_func()

You can also try this code with Online Python Compiler
Run Code
Output:
Inner x: 20
Outer x: 20
In this case, we define x as a single-element list in outer_func(). Inside inner_func(), we modify the value of x[0] directly. Since lists are mutable, this change affects the list in the outer scope as well.
Another alternative is to use function arguments to pass values between scopes. Instead of relying on nonlocal variables, you can explicitly pass the required data into the inner function as arguments and return any necessary results.
For example:
def outer_func():
x = 10
def inner_func(x):
x = 20
print(f"Inner x: {x}")
return x
x = inner_func(x)
print(f"Outer x: {x}")
outer_func()

You can also try this code with Online Python Compiler
Run Code
Output:
Inner x: 20
Outer x: 20
In this approach, we pass x as an argument to inner_func(), modify its value inside the function, and then return the new value. We then assign the returned value back to x in outer_func().
Note: These alternatives can be helpful in situations where using nonlocal might make the code less readable or harder to understand. However, there are still cases where nonlocal is the most appropriate choice, especially when you need to modify multiple variables or have more complex nested structures.
Troubleshooting Python Nonlocal: Common Issues and Solutions
When using the nonlocal keyword in Python, you might encounter a few common issues. Understanding these issues and knowing how to resolve them can save you a lot of debugging time.
One common mistake is forgetting to declare a variable as nonlocal before using it. If you try to assign a value to a variable without first declaring it as nonlocal, Python will create a new local variable instead of modifying the outer variable.
Let’s discuss an example of this mistake:
def outer_func():
x = 10
def inner_func():
x = 20 # Oops! Forgot to declare x as nonlocal
print(f"Inner x: {x}")
inner_func()
print(f"Outer x: {x}")
outer_func()

You can also try this code with Online Python Compiler
Run Code
Output:
Inner x: 20
Outer x: 10
To fix this issue, you need to explicitly declare the variable as nonlocal before assigning to it:
def outer_func():
x = 10
def inner_func():
nonlocal x
x = 20
print(f"Inner x: {x}")
inner_func()
print(f"Outer x: {x}")
outer_func()

You can also try this code with Online Python Compiler
Run Code
Output:
Inner x: 20
Outer x: 20
Another issue you might face is using nonlocal on a variable that doesn't exist in an outer scope. Python will raise a SyntaxError if you try to declare a variable as nonlocal when no such variable exists in any enclosing scope.
For example:
def outer_func():
def inner_func():
nonlocal y # Error! y is not defined in any outer scope
y = 20
inner_func()
outer_func()
To avoid this issue, make sure that the variable you're declaring as nonlocal is actually defined in an outer function scope.
One more thing to watch out for is using nonlocal in a way that makes your code harder to understand. Overusing nonlocal or modifying variables from distant scopes can make your code less readable and more error-prone.
Let’s take an example of code that can be confusing:
def outer_func():
x = 10
def inner_func1():
nonlocal x
x = 20
def inner_func2():
nonlocal x
x = 30
inner_func2()
inner_func1()
print(f"Outer x: {x}")
outer_func()

You can also try this code with Online Python Compiler
Run Code
While this code works, it's not immediately clear which function is modifying x and in what order. In cases like this, it might be better to use function arguments and return values to pass data between scopes, or to break up the nested functions into separate, top-level functions.
Frequently Asked Questions
What happens if I don’t use nonlocal inside a nested function?
If you don’t use nonlocal, Python will treat the variable in the nested function as a local variable, meaning it won’t modify the variable in the outer scope.
Can I use nonlocal with global variables?
No, nonlocal only works with variables in enclosing functions. To modify global variables, you must use the global keyword.
Is nonlocal always necessary for nested functions?
No, it’s only necessary when you want to modify a variable in the outer function scope. If you don’t need to modify it, you can simply access it without using nonlocal.
Conclusion
The nonlocal keyword in Python is a powerful tool that allows you to modify variables from an outer, non-global scope. It is especially useful for maintaining state between nested functions and avoiding the use of global variables. However, it should be used carefully, as it can introduce complexity into your code, especially in large programs.
You can also check out our other blogs on Code360.