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.