为什么在全局范围而不是封闭范围中查找未初始化的类变量?

Kri*_*oss 4 python variables class function

这不是我编码项目需要的问题,但我的一个朋友把这个问题发给了我。问题是:这里打印了什么?为什么?

x = 0
y = 0
def f():
    x = 1
    y = 1
    class A:
        print(x,y)
        x = 2
f()
Run Code Online (Sandbox Code Playgroud)

输出:

0 1
Run Code Online (Sandbox Code Playgroud)

我的第一直觉告诉我应该是“1 1”,但我错了。我摆弄了代码,发现如果我删除“x = 2”,我会是正确的。但为什么?

此外,当我将变量设为全局时,它也起作用了。像这样:

x = 0
y = 0
def f():
    global x
    global y
    x = 1
    y = 1
    class A:
        print(x,y)
        x = 2
f()
Run Code Online (Sandbox Code Playgroud)

输出:

1 1
Run Code Online (Sandbox Code Playgroud)

我不是在寻找问题的其他解决方案,只是更好地了解这里发生的事情。

提前致谢

Thi*_*lle 10

这记录在名称解析的最后一段中:

exec() 和 eval() 的类定义块和参数在名称解析的上下文中是特殊的。类定义是可以使用和定义名称的可执行语句。除了在全局命名空间中查找未绑定的局部变量之外,这些引用遵循名称解析的正常规则。(强调我的)

因此,y正如预期的那样,在定义它的最近的封闭范围(f函数,在那里它获得值 1)中查找。

另一方面,x = 2导致 x 被认为是本地的,并且由于它尚未在 中定义print(x,y),异常导致它在全局命名空间(它获得值 0)中查找,而不是导致 a NameError,就像正常函数中的未绑定局部变量是预期的。


我一直想知道为什么这个特殊规则存在,多亏了@orlp 在问题下的评论,我才找到了答案。

问题中的代码似乎来自 KMOD'S BLOG 的 2014 年博客文章,最近由 Guido van Rossum 发布

当有人问及原因时,Guido 回答说

向后兼容 Python 2.1。

所以现在我们知道...