问题
def some_func(a):
def access_a():
print(a)
access_a()
outputs the value of a
. However, if I want to change a
in the nested function like this:
def some_func(a):
def change_a():
a += 1
print(a)
change_a()
it raises UnboundLocalError
exception.
I know a
is a nonlocal variable, but why can I access it without declaring nonlocal a
?
回答1:
Python scoping rules 101:
a name bound in a function body is considered
local
unless explicitely declaredglobal
(Python 2.x and 3.x) ornonlocal
(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).
回答2:
i have two solutions for you:
#first one:
# try with list, compound data types dict/list
def some_func(a):
def change_a():
a[0] += 1
print(a[0])
change_a()
some_func([1])
>>> 2
#second one
#reference pointer
from ctypes import *
def some_func_ctypes(a):
def change_a():
a[0] += 1
print a.contents, a[0]
change_a()
i = c_int(1)
pi = pointer(i)
some_func_ctypes(pi)
>>> c_int(2) 2
回答3:
When you use the +=
operator, there is an assignment of a new value to a
. This turns a to a local in the eyes of the interpreter.
来源:https://stackoverflow.com/questions/41278818/nested-function-change-variable-in-an-outside-function-not-working