繁体   English   中英

python中奇怪的作用域行为

[英]Weird scoping behavior in python

考虑以下python代码片段:

x = 1
class Foo:
    x = 2
    def foo():
        x = 3
        class Foo:
            print(x) # prints 3
Foo.foo()

正如预期的那样,这会打印 3。但是,如果我们在上面的代码片段中添加一行,行为就会改变:

x = 1
class Foo:
    x = 2
    def foo():
        x = 3
        class Foo:
            x += 10
            print(x) # prints 11
Foo.foo()

而且,如果我们在上面的例子中切换两行的顺序,结果又会发生变化:

x = 1
class Foo:
    x = 2
    def foo():
        x = 3
        class Foo:
            print(x) # prints 1
            x += 10
Foo.foo()

我想了解为什么会发生这种情况,更一般地说,了解导致这种行为的范围规则。 根据 LEGB 范围规则,我希望两个片段都打印 3、13 和 3,因为在封闭函数foo()定义了一个x

类块作用域是特殊的。 它记录在这里

类定义是可以使用和定义名称的可执行语句。 这些引用遵循名称解析的正常规则,例外是在全局命名空间中查找未绑定的局部变量。 类定义的命名空间成为类的属性字典。 类块中定义的名称范围仅限于类块; 它没有扩展到方法的代码块——这包括推导式和生成器表达式,因为它们是使用函数作用域实现的。

基本上,类块不“参与”创建/使用封闭范围。

所以,它实际上 是第一个没有按照文档工作的例子 我认为这是一个实际的错误。

编辑:

好的,所以实际上,这是来自数据模型的一些更相关的文档,我认为这实际上与文档一致:

类主体作为 exec(body, globals(), namespace) 执行(大约)。 与普通调用 exec() 的主要区别在于,词法作用域允许类主体(包括任何方法)在类定义出现在函数内部时从当前作用域和外部作用域引用名称。

所以类块确实参与使用封闭范围,但对于自由变量(无论如何都是正常的)。 在我引用的第一篇文档中,关于“在全局命名空间中查找未绑定的局部变量”的部分适用于通常由 compiler 标记为 local 的变量。 因此,请考虑这个臭名昭著的错误,例如:

x = 1
def foo():
    x += 1
    print(x)

foo()

会抛出一个未绑定的本地错误,但是一个等效的类定义:

x = 1
class Foo:
    x += 1
    print(x)

将打印2

基本上,如果在类块中的任何地方都有赋值语句,它是“本地的”,但它会在全局范围内检查是否存在未绑定的本地而不是抛出UnboundLocal错误。

因此,在您的第一个示例中,它不是局部变量,它只是一个自由变量,并且解析遵循正常规则。 在接下来的两个示例中,您使用了一个赋值语句,将x标记为“本地”,因此,如果它在本地命名空间中未绑定,它将在全局命名空间中查找。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM