Why can't Python increment variable in closure?

closures, python

Solution

You can't re-bind 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

bar = foo()
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

bar = foo()
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. Define `__call__()` to make the instance callable.

class Foo(object):

    def __init__(self, counter=1):
       self.counter = counter

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

bar = Foo()
bar()

For completeness, I'll suggest that you could also write the function as a generator using the last `gen2func` decorator in this answer of mine. This lets you use regular local variables to maintain state across calls, because the function isn't really a function and never actually exits. However, this is more a "ain't that cool" than a recommended solution!

Problem

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?

Original source

Related problems