Today, while spelunking across Python's documentation, I discovered a new statement which isn't very commonly known or used, nonlocal
. Let's see what it is and what it does.
nonlocal
statement is pretty old and was introduced by PEP-3014 and allows a variable to re-bind to a scope other than local and global, which is the enclosing scope. What does that mean? Consider this Python function:
def function():
x = 100
def incr_print(y):
print(x + y)
incr_print(100)
Trying to run this function will give you the expected output:
In [5]: function()
200
In this case, we see that the inner function, incr_print
is able to read the value of x
from it's outer scope i.e. function
.
Now consider this function instead:
def function():
x = 100
def incr(y):
x = x + y
incr(100)
It is pretty simple, but when you try to run it, it fails with:
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-2-30ca0b4348da> in <module>
----> 1 function()
<ipython-input-1-61421989fe16> in function()
3 def increment(y):
4 x = x + y
----> 5 increment(100)
6
<ipython-input-1-61421989fe16> in increment(y)
2 x = 100
3 def increment(y):
----> 4 x = x + y
5 increment(100)
6
UnboundLocalError: local variable 'x' referenced before assignment
So, you can read from the variable in the outer scope, but you can't write to it because Python won't allow re-binding an object in outer scope. But, there is a way out of this, you must have read about global
scope:
In [6]: z = 100
In [8]: def function():
...: global z
...: z = z + 100
...:
In [9]: function()
In [10]: z
Out[10]: 200
So, you can actually refer to global scope using the global
statement. So, to fill in the gap for re-binding to outer scope, a new nonlocal
was added:
def function():
x = 100
def incr(y):
nonlocal x
x = x + y
incr(100)
print(x)
When you run this:
In [13]: function()
200
You can read more details in the documentation and the PEP 3104 itself.
Thanks to Mario Jason Braganza for proof-reading and pointing out typos in this post.