What does "nonlocal" do in Python 3?

Question:

What does nonlocal do in Python 3.x?


To close debugging questions where OP needs nonlocal and doesn’t realize it, please use Is it possible to modify variable in python that is in outer, but not global, scope? instead.

Although Python 2 is officially unsupported as of January 1, 2020, if for some reason you are forced to maintain a Python 2.x codebase and need an equivalent to nonlocal, see nonlocal keyword in Python 2.x.

Asked By: ooboo

||

Answers:

In short, it lets you assign values to a variable in an outer (but non-global) scope. See PEP 3104 for all the gory details.

Answered By: Arkady

A google search for “python nonlocal” turned up the Proposal, PEP 3104, which fully describes the syntax and reasoning behind the statement. in short, it works in exactly the same way as the global statement, except that it is used to refer to variables that are neither global nor local to the function.

Here’s a brief example of what you can do with this. The counter generator can be rewritten to use this so that it looks more like the idioms of languages with closures.

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

Obviously, you could write this as a generator, like:

def counter_generator():
    count = 0
    while True:
        count += 1
        yield count

But while this is perfectly idiomatic python, it seems that the first version would be a bit more obvious for beginners. Properly using generators, by calling the returned function, is a common point of confusion. The first version explicitly returns a function.

Compare this, without using nonlocal:

x = 0
def outer():
    x = 1
    def inner():
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 0

To this, using nonlocal, where inner()‘s x is now also outer()‘s x:

x = 0
def outer():
    x = 1
    def inner():
        nonlocal x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 2
# global: 0

If we were to use global, it would bind x to the properly "global" value:

x = 0
def outer():
    x = 1
    def inner():
        global x
        x = 2
        print("inner:", x)
        
    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 2
Answered By: Anon

It takes the one "closest" to the point of reference in the source code.
This is called "Lexical Scoping" and is standard for >40 years now.

Python’s class members are really in a dictionary called __dict__ and will never be reached by lexical scoping.

If you don’t specify nonlocal but do x = 7, it will create a new local variable "x".
If you do specify nonlocal, it will find the "closest" "x" and assign to that.
If you specify nonlocal and there is no "x", it will give you an error message.

The keyword global has always seemed strange to me since it will happily ignore all the other "x" except for the outermost one.

Answered By: Danny Milosavljevic

My personal understanding of the “nonlocal” statement (and do excuse me as I am new to Python and Programming in general) is that the “nonlocal” is a way to use the Global functionality within iterated functions rather than the body of the code itself. A Global statement between functions if you will.

Answered By: Yossi Truzman

help(‘nonlocal’)
The nonlocal statement


    nonlocal_stmt ::= "nonlocal" identifier ("," identifier)*

The nonlocal statement causes the listed identifiers to refer to
previously bound variables in the nearest enclosing scope. This is
important because the default behavior for binding is to search the
local namespace first. The statement allows encapsulated code to
rebind variables outside of the local scope besides the global
(module) scope.

Names listed in a nonlocal statement, unlike to those listed in a
global statement, must refer to pre-existing bindings in an
enclosing scope (the scope in which a new binding should be created
cannot be determined unambiguously).

Names listed in a nonlocal statement must not collide with pre-
existing bindings in the local scope.

See also:

PEP 3104 – Access to Names in Outer Scopes
The specification for the nonlocal statement.

Related help topics: global, NAMESPACES

Source: Python Language Reference

Answered By: Yossi Truzman
a = 0    #1. global variable with respect to every function in program

def f():
    a = 0          #2. nonlocal with respect to function g
    def g():
        nonlocal a
        a=a+1
        print("The value of 'a' using nonlocal is ", a)
    def h():
        global a               #3. using global variable
        a=a+5
        print("The value of a using global is ", a)
    def i():
        a = 0              #4. variable separated from all others
        print("The value of 'a' inside a function is ", a)

    g()
    h()
    i()
print("The value of 'a' global before any function", a)
f()
print("The value of 'a' global after using function f ", a)
Answered By: gxyd

with ‘nonlocal’ inner functions(ie;nested inner functions) can get read & ‘write‘ permission for that specific variable of the outer parent function. And nonlocal can be used only inside inner functions
eg:

a = 10
def Outer(msg):
    a = 20
    b = 30
    def Inner():
        c = 50
        d = 60
        print("MU LCL =",locals())
        nonlocal a
        a = 100
        ans = a+c
        print("Hello from Inner",ans)       
        print("value of a Inner : ",a)
    Inner()
    print("value of a Outer : ",a)

res = Outer("Hello World")
print(res)
print("value of a Global : ",a)
Answered By: NIPHIN

Quote from the Python 3 Reference:

The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals.

As said in the reference, in case of several nested functions only variable in the nearest enclosing function is modified:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        x = 2
        innermost()
        if x == 3: print('Inner x has been modified')

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Inner x has been modified

The “nearest” variable can be several levels away:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Outer x has been modified

But it cannot be a global variable:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    inner()

x = 0
outer()
if x == 3: print('Global x has been modified')

# SyntaxError: no binding for nonlocal 'x' found
Answered By: Jeyekomon

The documentation says below:

The nonlocal statement causes the listed identifiers to refer to
previously bound variables in the nearest enclosing scope excluding
globals. …

So for example, nonlocal foo in inner() can access the non-local variable foo = 10 in middle() but not the non-local variable foo = 5 in outer() or the global variable foo = 0 outside outer() as shown below:

foo = 0 # <- ✖
def outer():
    foo = 5 # <- ✖
    def middle():
        foo = 10 # <- 〇
        def inner():
            nonlocal foo # Here
            foo += 1
            print(foo) # 11
        inner()
    middle()
outer()
Answered By: Kai – Kazuya Ito