Python scoping rules 101:
a name bound in a function body is considered local
unless explicitely declared global
(Python 2.x and 3.x) or nonlocal
(Python 3.x only). This holds true whereever the assignment happens in the function's body. Trying to read a local variable before it's bound is of course an error.
if a name is read but not bound in a function's body, it will be looked up in enclosing scopes (outer function(s) if any then global scope). NB: functions arguments are de facto local names so they will never be looked up in enclosing scopes.
Note that a += 1
is mainly a shortcut for a = a + 1
, so in your example a
is local (bound in the function's body and not explicitely declared global or nonlocal), but you try to read it (the rhs of a = a+1
) before it's bound.
In Python 3 you can solve this with a nonlocal
statement:
>>> def outer(a):
... def change():
... nonlocal a
... a += 1
... print("before : {}".format(a))
... change()
... print ("after : {}".format(a))
...
>>> outer(42)
before : 42
after : 43
Python 2 doesn't have nonlocal
so the canonical hack is to wrap the variable in a mutable container (typically a list
but any mutable object would do):
>>> def outer(a):
... _a = [a]
... def change():
... _a[0] += 1
... print("before : {}".format(_a[0]))
... change()
... print ("after : {}".format(_a[0]))
...
>>> outer(42)
before : 42
after : 43
which is quite ugly to say the least.
Now while closures are quite handy, they are mostly the functional counterpart of objects : a way to share state between a set of functions while preserving encapsulation of this state, so if you find you have a need for a nonlocal
variable perhaps a proper class might be a cleaner solution (though possibly not for your example that doesn't return the inner function but only uses it internally).