在 Python 中将自由变量视为全局变量?

Free variables treated as globals in Python?

在 Python 3.7 参考手册的 Execution Model section 中,我阅读了以下声明:

The global statement has the same scope as a name binding operation in the same block. If the nearest enclosing scope for a free variable contains a global statement, the free variable is treated as a global.

所以我在 Python 解释器中输入了以下代码:

x =0
def func1():
    global x
    def func2():
        x = 1
    func2()

在调用 func1() 之后,我预计 x 在全局范围内的值会更改为 1

我做错了什么?

x = 1 in func2 不是自由变量。这只是另一个本地人;你绑定到名称,默认情况下,绑定到的名称是本地名称,除非你告诉 Python 否则。

来自same Execution model documentation:

If a name is bound in a block, it is a local variable of that block, unless declared as nonlocal or global. [...] If a variable is used in a code block but not defined there, it is a free variable.

(大胆强调我的)

你用x = 1绑定了块中的名称,因此它是该块中的局部变量,不能是自由变量。所以你找到的部分不适用,因为那只适用于自由变量:

If the nearest enclosing scope for a free variable contains a global statement, the free variable is treated as a global.

您不应在 func2() 中绑定 x,因为只有在范围内 未绑定 的名称才是自由变量。

所以这个有效:

>>> def func1():
...     global x
...     x = 1
...     def func2():
...         print(x)  # x is a free variable here
...     func2()
...
>>> func1()
1
>>> x
1

x in func2 现在是一个自由变量;它未在 func2 的范围内定义,因此从父范围中选取它。这里的父作用域是 func1,但是 x 在那里被标记为全局,所以当 reading x 用于 print() 函数时全局使用值。

将此与 xfunc1 中未标记为全局进行对比:

>>> def func1():
...     x = 1
...     def func2():
...         print(x)  # x is free variable here, now referring to x in func1
...     func2()
...
>>> x = 42
>>> func1()
1

此处全局名称x设置为42,但这并不影响打印的内容。 func2中的x是一个自由变量,但是父作用域func1只有x作为局部名称。

当您添加一个新的最外层范围时,x 仍然是本地,它变得更加有趣

>>> def outerfunc():
...     x = 0   # x is a local
...     def func1():
...         global x   # x is global in this scope and onwards
...         def func2():
...             print('func2:', x)  # x is a free variable
...         func2()
...     print('outerfunc:', x)
...     func1()
...
>>> x = 42
>>> outerfunc()
outerfunc: 0
func2: 42
>>> x = 81
>>> outerfunc()
outerfunc: 0
func2: 81

x in outerfunc 是绑定的,所以不是自由变量。因此,它在该范围内是本地的。但是,在 func1 中,global x 声明将 x 标记为嵌套范围中的全局变量。在 func2 x 中是一个自由变量,根据您找到的语句,它被视为全局变量。