5
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

4

3 回答 3

9

Python 范围规则 101:

  1. local除非明确声明global(Python 2.x 和 3.x)或nonlocal(仅限 Python 3.x ),否则考虑绑定在函数体中的名称。这适用于函数体中发生赋值的任何地方。在绑定之前尝试读取局部变量当然是错误的。

  2. 如果读取了名称但未绑定在函数主体中,则将在封闭范围(如果有外部函数,则为全局范围)中查找该名称。注意:函数参数实际上是本地名称,因此永远不会在封闭范围内查找它们。

请注意,这a += 1主要是 的快捷方式a = a + 1,因此在您的示例a中是本地的(绑定在函数的主体中,并且没有明确声明为全局或非本地),但是您尝试在a = a+1绑定之前读取它( rhs )。

nonlocal在 Python 3 中,您可以使用以下语句解决此问题:

>>> 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变量,也许一个合适的类可能是一个更清洁的解决方案(尽管可能不适用于不返回内部函数但仅在内部使用它的示例)。

于 2016-12-22T09:33:18.027 回答
1

我有两个解决方案给你:

#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
于 2016-12-22T09:44:49.433 回答
0

当您使用+=运算符时,会为 . 分配一个新值a。这在口译员的眼中变成了当地人。

于 2016-12-22T08:37:55.207 回答