外部函数中的嵌套函数更改变量不起作用
nested function change variable in an outside function not working
def some_func(a):
def access_a():
print(a)
access_a()
输出a
的值。但是,如果我想像这样在嵌套函数中更改 a
:
def some_func(a):
def change_a():
a += 1
print(a)
change_a()
它引发了 UnboundLocalError
异常。
我知道 a
是一个非局部变量,但为什么我可以在不声明 nonlocal a
的情况下访问它?
当您使用 +=
运算符时,会为 a
分配一个新值。这在口译员眼中变成了当地人。
Python 范围规则 101:
函数体中绑定的名称被视为 local
除非明确声明 global
(Python 2.x 和 3.x)或 nonlocal
(仅限 Python 3.x)。无论赋值发生在函数体中的什么地方,这都适用。在绑定之前尝试读取局部变量当然是错误的。
如果一个名称被读取但未绑定在函数体中,它将在封闭范围(如果有则为全局范围的外部函数)中查找。注意:函数参数实际上是本地名称,因此永远不会在封闭范围内查找它们。
请注意 a += 1
主要是 a = a + 1
的快捷方式,因此在您的示例中 a
是局部的(绑定在函数的主体中并且未明确声明为全局或非局部),但是你尝试在绑定之前阅读它(a = a+1
的右侧)。
在 Python 3 中,您可以使用 nonlocal
语句解决此问题:
>>> 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 没有 nonlocal
所以规范的 hack 是将变量包装在可变容器中(通常是 list
但任何可变对象都可以):
>>> 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
至少可以说这很丑陋。
现在虽然闭包非常方便,但它们主要是对象的功能对应物:一种在一组函数之间共享状态同时保留此状态封装的方法,因此如果您发现需要 nonlocal
变量也许一个合适的 class 可能是一个更清晰的解决方案(尽管对于你的例子来说可能不是 return 内部函数但只在内部使用它)。
我有两个解决方案给你:
#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
def some_func(a):
def access_a():
print(a)
access_a()
输出a
的值。但是,如果我想像这样在嵌套函数中更改 a
:
def some_func(a):
def change_a():
a += 1
print(a)
change_a()
它引发了 UnboundLocalError
异常。
我知道 a
是一个非局部变量,但为什么我可以在不声明 nonlocal a
的情况下访问它?
当您使用 +=
运算符时,会为 a
分配一个新值。这在口译员眼中变成了当地人。
Python 范围规则 101:
函数体中绑定的名称被视为
local
除非明确声明global
(Python 2.x 和 3.x)或nonlocal
(仅限 Python 3.x)。无论赋值发生在函数体中的什么地方,这都适用。在绑定之前尝试读取局部变量当然是错误的。如果一个名称被读取但未绑定在函数体中,它将在封闭范围(如果有则为全局范围的外部函数)中查找。注意:函数参数实际上是本地名称,因此永远不会在封闭范围内查找它们。
请注意 a += 1
主要是 a = a + 1
的快捷方式,因此在您的示例中 a
是局部的(绑定在函数的主体中并且未明确声明为全局或非局部),但是你尝试在绑定之前阅读它(a = a+1
的右侧)。
在 Python 3 中,您可以使用 nonlocal
语句解决此问题:
>>> 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 没有 nonlocal
所以规范的 hack 是将变量包装在可变容器中(通常是 list
但任何可变对象都可以):
>>> 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
至少可以说这很丑陋。
现在虽然闭包非常方便,但它们主要是对象的功能对应物:一种在一组函数之间共享状态同时保留此状态封装的方法,因此如果您发现需要 nonlocal
变量也许一个合适的 class 可能是一个更清晰的解决方案(尽管对于你的例子来说可能不是 return 内部函数但只在内部使用它)。
我有两个解决方案给你:
#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