Neil Neil - 2 days ago 4
Python Question

Why can't Python increment variable in closure?

In this code snippet I can print the value of counter from inside the bar function

def foo():
counter = 1
def bar():
print("bar", counter)
return bar

bar = foo()

bar()


But when I try to increment counter from inside the bar function I get an UnboundLocalError error.

UnboundLocalError: local variable 'counter' referenced before assignment


Code snippet with increment statement in.

def foo():
counter = 1
def bar():
counter+=1
print("bar", counter)
return bar

bar = foo()

bar()


Do you only have read access to variables in the outer function in a Python closure?

Answer

You can't mutate closure variables in Python 2. In Python 3, which you appear to be using due to your print(), you can declare them nonlocal:

def foo():

  counter = 1

  def bar():
    nonlocal counter
    counter += 1
    print("bar", counter)

  return bar

Otherwise, the assignment within bar() makes the variable local, and since you haven't assigned a value to the variable in the local scope, trying to access it is an error.

In Python 2, my favorite workaround looks like this:

def foo():

  class nonlocal:
    counter = 1

  def bar():
    nonlocal.counter += 1
    print("bar", nonlocal.counter)

  return bar

This works because mutating a mutable object doesn't require changing what the variable name points to. In this case, nonlocal is the closure variable and it is never reassigned; only its contents are changed. Other workarounds use lists or dictionaries.

Or you could use a class for the whole thing, as @naomik suggests in a comment.

Comments