Daniel Moerner

When do you need nonlocal or global in Python?

I recently realized I had a slightly hazy understanding of name scope in Python. I knew I needed to use global or nonlocal in some instances, but I wasn’t exactly clear when and where they were needed. In practice this means scattering them everywhere they might be used.

I think I figured it out, and it’s really quite simple, but I didn’t find as clear a statement as I would have liked in the documentation. This post draws on the official Python 3 documentation and PEP 3104 - Access to Names in Outer Scopes, as well as my own testing.

The basic rules are simple:

  1. Python code can refer to any name in any enclosing scope, without a global or nonlocal declaration. Python will search upwards, starting with the current scope, looking for the closest assigned name it can find.
  2. Python code can assign to a new name only within its local scope.
  3. Python code can rebind a name only within its local scope, unless the name within the local scope has previously been given the nonlocal keyword or global keyword.

What does nonlocal do? It “prevents x from becoming a local name in the current scope. All occurrences of x in the current scope will refer to the x bound in an outer enclosing scope” (PEP 3104). That’s “an” outer enclosing scope, i.e., it looks upward to the closest scope with the name bound, or raises an exception if none can be found. It raises an exception in such a case because of Rule 2: Even with one of these keywords, you can’t assign a new name in a higher scope, you can only rebind an existing one.

What is an assignment or rebinding in this context? It is anything with an =. Remember that in Python this operator is overloaded to handle both assignment and rebinding. (Although there are other operators that will assign or rebind, like as in with or except blocks, those are all locally scoped, as far as I know.)

So here’s the TLDR practical rule:

  • If your variable is on the left side of =, and you are intending to rebind something outside the local scope, you need either nonlocal or global as appropriate. Otherwise, you should not use them.

Some toy examples. First, a case where nonlocal is needed:

def test1():
    x = 1
    def innertest1():
        print(f"Inner, can refer to any name in an enclosing scope: Expected 1, got: {x}")
    def innertest2():
        nonlocal x
        x = 2
    innertest1()
    innertest2()
    print(f"Outer, after rebinding with `=`: Expected: 2, got: {x}")

Second, a case where nonlocal is not needed:

def test2():
    x = []
    def innertest1():
        x.append(1)
    innertest1()
    print(f"Outer, `append` does not rebind: Expected: [1], got: {x}")

This second pattern is of course common with backtracking algorithms. When appending to a list of combinations, nonlocal is not needed.