Nonlocal: basic-level questions

If you need help reviewing Nonlocal, take a look at these resources:

Each question has a "Toggle Solution" button -- click it to reveal that question's solution.

Conceptual Questions

Question 1

Would this code work? If not, how would you fix it?

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

No, this code would not work. Here's how we can find out:

  • The line count += 1 is equivalent to count = count + 1, so we rewrite it as such.
  • Python notices that count appears on the left side of an assignment statement, so Python remembers to treat count as a local variable.
  • Python then begins executing the line. To compute count + 1 Python must look up count.
  • But Python had previously marked count as a local variable, and it doesn't have a value yet! So Python raises an error.

To fix it, add a nonlocal statement:

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

Question 2

Consider the following code:

def fn1(bob):
    def fn2(alice):
        def fn3(alice):
            def fn4():
                nonlocal bob, alice
                return bob + alice
            return fn4
        return fn3
    return fn2

Answer the following questions:

  1. In which function's frame does Python start looking for alice?
  2. In which function's frame does Python stop looking for alice?
  3. In which function's frame does Python start looking for bob?
  4. In which function's frame does Python stop looking for bob?
  1. fn3
  2. fn3
  3. fn3
  4. fn1

Question 3

Identify all the errors regarding nonlocal in the following code:

bob = 2
def fn1(bob):
    eve = 3
    def fn2(alice):
        nonlocal bob, alice
        eve = 4
        return eve + alice
    return fn2

nonlocal alice is incorrect, since alice is already defined in the same frame (as a parameter to fn2).

eve = 4 will NOT cause any errors, since eve is not being referenced before assignment. However, because eve is not declared as nonlocal, the eve in fn1 will retain the value of 3.

Environment Diagrams

Question 4

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

counter = make_counter()
counter()
counter()

Question 5

def foo():
    lst = []
    def bar(m):
        nonlocal lst
        lst = lst + [m]
        return lst
    return bar

bar = foo()
bar(3)
bar(4)

Question 6

def foo():
    lst = []
    def bar(m):
        lst.append(m)
        return lst
    return bar

bar = foo()
bar(3)
bar(4)