python中奇怪的作用域行为 [英] Weird scoping behavior in python

查看:59
本文介绍了python中奇怪的作用域行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下 Python 代码片段:

Consider the following snippet of python code:

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

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

As expected, this prints 3. But, if we add a single line to the above snippet, the behavior changes:

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

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

And, if we switch the order of the two lines in the above example, the result changes yet again:

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.>

I'd like to understand why this occurs, and more generally, understand the scoping rules that cause this behavior. From the LEGB scoping rule, I would expect that both snippets print 3, 13, and 3, since there is an x defined in the enclosing function foo().

推荐答案

类块作用域是特殊的.它记录在此处:

Class block scope is special. It is documented here:

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

A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace. The namespace of the class definition becomes the attribute dictionary of the class. The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes comprehensions and generator expressions since they are implemented using a function scope.

基本上,类块不会参与"在创建/使用封闭范围.

Basically, class blocks do not "participate" in creating/using enclosing scopes.

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

So, it is actually the first example that isn't working as documented. I think this is an actual bug.

好的,实际上,这里有一些来自 数据模型,我认为这一切实际上与文档一致:

OK, so actually, here's some more relevant documentation from the data model, I think it all is actually consistent with the documentation:

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

The class body is executed (approximately) as exec(body, globals(), namespace). The key difference from a normal call to exec() is that lexical scoping allows the class body (including any methods) to reference names from the current and outer scopes when the class definition occurs inside a function.

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

So class blocks do participate in using enclosing scopes, but for free variables (as is normal anyway). In the first piece of documentation that I'm quoting, the part about "unbound local variables are looked up in the global namespace" applies to variables that would normally be marked local by the complier. So, consider this notorious error, for example:

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

foo()

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

Would throw an unbound local error, but an equivalent class definition:

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

将打印2.

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

Basically, if there is an assignment statement anywhere in a class block, it is "local", but it will check in the global scope if there is an unbound local instead of throwing the UnboundLocal error.

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

Hence, in your first example, it isn't a local variable, it is simply a free variable, and the resolution goes through the normal rules. In your next two examples, you us an assignment statemnt, marking x as "local", and thus, it will be looked up in the global namespace in case it is unbound in the local one.

这篇关于python中奇怪的作用域行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆