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?
Python Decorators make an extensive use of closures as well. On a concluding note, it is good to point out that the values that get enclosed in the closure function can be found out. All function objects have a __closure__ attribute that returns a tuple of cell objects if it is a closure function.
Closure is not the inner function. the meaning of closure is act of closing. So inner function is closing over a nonlocal variable which is called free variable.
without using global you can't modify the value of a global variable inside a function, you can only use it's value inside the function. But if you want to assign a new value to it then you've to use the global keyword first.
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()
Why can't Python increment variable in closure?
I offer a couple of solutions here.
nonlocal
(ideal, but Python 3 only)__call__
Set a counter attribute on your function manually after creating it:
def foo(): foo.counter += 1 return foo.counter foo.counter = 0
And now:
>>> foo() 1 >>> foo() 2 >>> foo() 3
Or you can auto-set the function:
def foo(): if not hasattr(foo, 'counter'): foo.counter = 0 foo.counter += 1 return foo.counter
Similarly:
>>> foo() 1 >>> foo() 2 >>> foo() 3
These approaches are simple, but uncommon, and unlikely to be quickly grokked by someone viewing your code without you present.
More common ways what you wish to accomplish is done varies depending on your version of Python.
nonlocal
In Python 3, you can declare nonlocal:
def foo(): counter = 0 def bar(): nonlocal counter counter += 1 print("bar", counter) return bar bar = foo()
And it would increment
>>> bar() bar 1 >>> bar() bar 2 >>> bar() bar 3
This is probably the most idiomatic solution for this problem. Too bad it's restricted to Python 3.
You could declare a global variable, and then increment on it, but that clutters the module namespace. So the idiomatic workaround to avoid declaring a global variable is to point to a mutable object that contains the integer on which you wish to increment, so that you're not attempting to reassign the variable name:
def foo(): counter = [0] def bar(): counter[0] += 1 print("bar", counter) return bar bar = foo()
and now:
>>> bar() ('bar', [1]) >>> bar() ('bar', [2]) >>> bar() ('bar', [3])
I do think that is superior to the suggestions that involve creating classes just to hold your incrementing variable. But to be complete, let's see that.
class Foo(object): def __init__(self): self._foo_call_count = 0 def foo(self): self._foo_call_count += 1 print('Foo.foo', self._foo_call_count) foo = Foo()
and now:
>>> foo.foo() Foo.foo 1 >>> foo.foo() Foo.foo 2 >>> foo.foo() Foo.foo 3
or even implement __call__
:
class Foo2(object): def __init__(self): self._foo_call_count = 0 def __call__(self): self._foo_call_count += 1 print('Foo', self._foo_call_count) foo = Foo2()
and now:
>>> foo() Foo 1 >>> foo() Foo 2 >>> foo() Foo 3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With