范围规则如何与类一起使用?

问题描述 投票:0回答:1

考虑以下 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,因为封闭函数

x
中定义了
foo()

python python-3.x scope
1个回答
33
投票

类块作用域很特殊。它记录在here

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

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

所以,这实际上是第一个没有按照记录工作的示例。我认为这是一个真正的错误。

编辑:

好吧,实际上,这里有一些来自数据模型的更多相关文档。我认为这一切实际上都与文档一致:

类主体(大约)作为 exec(body, globals(), 命名空间)。与普通调用 exec() 的主要区别在于 词法作用域允许类体(包括任何方法) 类时来自当前和外部范围的引用名称 定义发生在函数内部。

因此类块 do 参与 using 封闭范围,但对于自由变量(无论如何都是正常的)。在我引用的第一个文档中,有关“在全局命名空间中查找未绑定局部变量”的部分适用于“通常由编译器标记为本地”的变量。因此,考虑一下这个臭名昭著的错误,例如: x = 1 def foo(): x += 1 print(x) foo()

这会引发未绑定的本地错误,但等效的类定义将打印 
2

:

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

基本上,如果类块中的任何位置有赋值语句,它就是“局部”的,但它会在全局范围内检查是否存在未绑定的局部变量,而不是抛出 
UnboundLocal

错误。

因此,在第一个示例中,它不是局部变量,它只是一个自由变量,并且解析遵循正常规则。在接下来的两个示例中,您使用赋值语句,将 

x

标记为“本地”,因此,如果它在本地命名空间中未绑定,它将在全局命名空间中查找。

    

© www.soinside.com 2019 - 2024. All rights reserved.