464

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.

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
ooboo
  • 16,259
  • 13
  • 37
  • 32
  • 4
    Take a look at this question: http://stackoverflow.com/questions/1414304/local-functions-in-python – Matt Joiner Dec 05 '10 at 13:50
  • 26
    Here is the official Python website documentation for nonlocal: http://docs.python.org/3/reference/simple_stmts.html#the-nonlocal-statement (this documentation has been available since Python 3.0, so the OP's assertion that there is no official documentation was just wrong) – wkschwartz Oct 03 '13 at 17:46
  • 4
    `"There is no documentation for nonlocal".` Actually, you can do `help(keyword_in_string)` for documentations in Python 3 and above – rassa45 Aug 07 '15 at 09:21
  • 16
    To be fair the official docs kind of suck on the subject. The selected answer's example makes things very clear, making this a valuable question. – Mad Physicist May 27 '16 at 20:14
  • 1
    In the official Python tutorial there is a [good explanation of the concept of scopes and namespaces](https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces) with a [nice example](https://docs.python.org/3/tutorial/classes.html#scopes-and-namespaces-example). – jammon Oct 08 '16 at 06:03
  • [Here](https://docs.python.org/3.6/tutorial/classes.html#scopes-and-namespaces-example) is the official documentation with example, and makes things pretty clear. – ksha Jan 29 '20 at 14:42
  • 1
    @MadPhysicist Why do you think the docs "suck"? Genuinely curious. I found `help('nonlocal')` to be pretty clear. – EntangledLoops Jul 23 '20 at 02:13
  • @EntangledLoops. I suspect that it was not so more than 5 years ago when I made that comment? – Mad Physicist Jul 23 '20 at 05:21
  • @MadPhysicist I might have thought that too, but there is an answer from about 9 years ago below that copy/pasted the text, and it looks the same as it appears today. – EntangledLoops Jul 24 '20 at 00:28
  • @EntangledLoops. Also, I didn't know much five years ago – Mad Physicist Jul 24 '20 at 15:16
  • Note for duplicate closers: This question explains what `nonlocal` does. For the reverse situation - OP needs `nonlocal` and doesn't realize it - consider https://stackoverflow.com/questions/8447947 instead. – Karl Knechtel Jul 03 '22 at 06:41

10 Answers10

640

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
wovano
  • 4,543
  • 5
  • 22
  • 49
Anon
  • 11,870
  • 3
  • 23
  • 19
  • 62
    Its very similar - but note that the outer x is not global in the example but is instead defined in the outer function. – Anon Aug 11 '09 at 18:04
  • so it lets you do inner classes that reference outer vars, like java? – Dustin Getz Aug 11 '09 at 18:07
  • 3
    @Dustin - Actually, if you had class A with an attribute x and a subclass B defined in it, you would refer to x from within B as A.x – Anon Aug 11 '09 at 18:37
  • Yes, Java inner classes have similar behaviour. – apt1002 Dec 12 '13 at 19:00
  • 3
    The code easily gets heavily indented when defining inner functions and ends up violating the 79 chars PEP8 recommendation. Any way to get around this problem? Can an inner function somehow be placed outside the outer function? I know the question sounds stupid, but I'm earnest. – tommy.carstensen Feb 12 '15 at 00:03
  • 5
    @tommy.carstensen you could pass the function as an arg that's the beauty of higher order functions. Also in functional programming this is called composition, python is not a pure FP language but you can certainly play with a features (generators, higher order functions are some examples) – superuseroi Mar 30 '15 at 21:24
  • so in essence the rule of thumb it to use `nonlocal` when an inner function will try to modify one of its closures? – pkaramol Jul 16 '19 at 12:29
  • 7
    What if there are 3 nested functions? Is there a way to access all 4 levels from within the innermost function then? – Rastapopoulos Oct 09 '20 at 12:11
  • It seems objects, such as arrays and dictionaries, work differently. They are nonlocal by default. Is that correct? What if I want to them local? – Hans Jul 21 '21 at 14:02
  • why name 3 variables in difference scopes, **with same name**? just to use `nonlocal`?i'd rather take a different name and avoid `nonlocal`. is there any benefit or something inevitable here? – Lei Yang Mar 03 '22 at 02:25
  • @LeiYang `nonlocal` is required in order to modify the "outer" `x`, *even if there is no* global `x`. Both `global` and `nonlocal` *prevent* `x` from being a local variable in `inner` - in these cases, it is *not actually* a separate variable. – Karl Knechtel Jul 03 '22 at 06:46
124

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.

Arkady
  • 14,305
  • 8
  • 42
  • 46
55

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.

SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
  • 1
    I was sure that's what the keyword 'global' does - works up higher enviornments until it reaches a variable with that name. a variable x could be declared at module level, inside a class, then separately in a function inside this class and then in an inner function of that function - how does it know which x to refer to? – ooboo Aug 11 '09 at 17:54
  • 15
    the thing about global is that it only works for global variables. it cannot see variables in an enclosing, nonglobal scope. – SingleNegationElimination Oct 11 '09 at 03:53
  • I tried the make_counter - however it doesn't return a generator but a function. is there a way to return a generator so later i could iterate over it? – Dejell Dec 05 '13 at 17:23
  • @Dejel: this example is intended to illustrate the `nonlocal` statement in Python; If you want a sequence of natural numbers, the python idiom is actually [`itertools.count()`](http://docs.python.org/2/library/itertools#itertools.count) – SingleNegationElimination Dec 05 '13 at 17:28
  • I would like to demo the ability to return a generator like with yield - yield actually returns a generator. My idea is not to use yield and instead maybe use nonlocal or another solution – Dejell Dec 05 '13 at 17:40
  • @Dejel: It sounds like you're having a problem that's a bit different from the one ooboo asked about. Please ask a new question instead of using the comments section here. – SingleNegationElimination Dec 05 '13 at 17:59
  • I think it is misleading to say, `it works in exactly the same way as the global statement`. Because `global x` can create a new global variable `x` if if did not existed, but `nonlocal x` must refer to an existing x variable in the enclosing scope. – Quazi Irfan Oct 10 '21 at 18:02
  • @ooboo "I was sure that's what the keyword 'global' does - works up higher enviornments until it reaches a variable with that name" No; it looks *only* at the *global* scope (and builtins, if you consider those separate), not in any intermediate scopes. Looking up a *local* will consider enclosing scopes, but anything not found locally will be *read-only*. – Karl Knechtel Sep 05 '22 at 10:06
24

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.

wovano
  • 4,543
  • 5
  • 22
  • 49
  • 1
    Note that if you _don't_ assign a new value, and instead only read it (e.g. `print(x)`), lexical scoping is the default and `nonlocal` makes no difference. – Nearoo Nov 14 '21 at 13:40
  • "The keyword global has always seemed strange to me since it will happily ignore all the other "x" except for the outermost one." I don't understand the reasoning. The outermost scope is global; the other ones are not. Why would `global` access them? – Karl Knechtel Sep 05 '22 at 10:09
17

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

user2314737
  • 27,088
  • 20
  • 102
  • 114
Yossi Truzman
  • 207
  • 2
  • 2
  • 17
    Learn something new every day. I had no idea you could use `help()` on keywords (and now my mind is blown: `help()` with no arguments goes _interactive_). – Erik Youngren Feb 24 '14 at 09:05
13

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
Jeyekomon
  • 2,878
  • 2
  • 27
  • 37
3
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)
gxyd
  • 667
  • 7
  • 13
2

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.

Yossi Truzman
  • 207
  • 2
  • 2
0

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)
NIPHIN
  • 1,071
  • 1
  • 8
  • 16
0

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()
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129