嵌套函数中的变量范围

Kan*_* Li 6 python scope nested-function

有人可以解释为什么以下程序失败:

def g(f):
  for _ in range(10):
    f()

def main():
  x = 10
  def f():
    print x
    x = x + 1
  g(f)

if __name__ == '__main__':
  main()
Run Code Online (Sandbox Code Playgroud)

随着消息:

Traceback (most recent call last):
  File "a.py", line 13, in <module>
    main()
  File "a.py", line 10, in main
    g(f)
  File "a.py", line 3, in g
    f()
  File "a.py", line 8, in f
    print x
UnboundLocalError: local variable 'x' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

但是,如果我只是将变量更改为x数组,它的工作原理如下:

def g(f):
  for _ in range(10):
    f()

def main():
  x = [10]
  def f():
    print x[0]
    x[0] = x[0] + 1
  g(f)

if __name__ == '__main__':
  main()
Run Code Online (Sandbox Code Playgroud)

与输出

10
11
12
13
14
15
16
17
18
19
Run Code Online (Sandbox Code Playgroud)

我感到困惑的原因是,如果从f()它无法访问x,为什么它变得可访问,如果x是一个数组?

谢谢.

7st*_*tud 5

但这个答案说问题出在分配给 x 上。如果是这样,那么打印它应该可以正常工作,不是吗?

你必须了解事情发生的顺序。在你的Python代码被编译和执行之前,有一个叫做读取 Python 代码并检查语法。解析器所做的另一件事是将变量标记为本地变量。当解析器在本地范围内的代码中看到赋值时,赋值左侧的变量将被标记为本地变量。此时,还没有编译任何内容,更不用说执行了,因此不会发生任何分配;该变量仅被标记为局部变量。

解析器完成后,将编译并执行代码。当执行到达打印语句时:

def main():
  x = 10     #<---x in enclosing scope

  def f():
    print x    #<-----

    x = x + 1  #<-- x marked as local variable inside the function f()
Run Code Online (Sandbox Code Playgroud)

print 语句看起来应该继续并打印 x 中的 x封闭范围内的 x(LEGB 查找过程中的“E”)。然而,由于解析器先前将 x 标记为 f() 内的局部变量,因此 python 不会继续越过局部范围(LEGB 查找过程中的“L”)来查找 x。由于执行“print x”时 x 尚未分配给本地范围,因此 python 会抛出错误。

请注意,即使发生赋值的代码永远不会执行,解析器仍然将赋值左侧的变量标记为局部变量。解析器不知道事情将如何执行,因此它会盲目地在整个文件中搜索语法错误和局部变量——即使是在永远无法执行的代码中。以下是一些例子:

def dostuff ():
    x = 10 

    def f():
        print x

        if False:  #The body of the if will never execute...
            a b c  #...yet the parser finds a syntax error here


    return f

f = dostuff()
f()



--output:--
File "1.py", line 8
     a b c
      ^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)

解析器在标记局部变量时做同样的事情:

def dostuff ():
    x = 10 

    def f():
        print x

        if False:  #The body of the if will never execute...
            x = 0  #..yet the parser marks x as a local variable

    return f

f = dostuff()
f()
Run Code Online (Sandbox Code Playgroud)

现在看看执行最后一个程序时会发生什么:

Traceback (most recent call last):
  File "1.py", line 11, in <module>
    f()
  File "1.py", line 4, in f
    print x
UnboundLocalError: local variable 'x' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

当语句“print x”执行时,由于解析器将 x 标记为局部变量,因此对 x 的查找将停止在局部范围内。

这个“特性”并不是 Python 所独有的——它也存在于其他语言中。

至于数组示例,当您编写:

x[0] = x[0] + 1
Run Code Online (Sandbox Code Playgroud)

它告诉 python 查找名为 x 的数组并将某些内容分配给它的第一个元素。由于在本地作用域中没有对任何名为 x 的内容进行赋值,因此解析器不会将 x 标记为本地变量。