Python 中如何解析对变量的引用 [英] How references to variables are resolved in Python

查看:56
本文介绍了Python 中如何解析对变量的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个消息有点长,例子很多,但我希望它将帮助我和其他人更好地掌握变量的完整故事和 Python 2.7 中的属性查找.

This message is a a bit long with many examples, but I hope it will help me and others to better grasp the full story of variables and attribute lookup in Python 2.7.

我正在使用 PEP 227 的条款(http://www.python.org/dev/peps/pep-0227/) 对于代码块(例如模块、类定义、函数定义等)和变量绑定(例如赋值、参数声明、类和函数声明、for 循环等)

I am using the terms of PEP 227 (http://www.python.org/dev/peps/pep-0227/) for code blocks (such as modules, class definition, function definitions, etc.) and variable bindings (such as assignments, argument declarations, class and function declaration, for loops, etc.)

我正在使用术语变量来表示可以在没有名称的情况下调用的名称点,以及需要用对象限定的名称的属性名称(如对象 obj 的属性 x 的 obj.x).

I am using the terms variables for names that can be called without a dot, and attributes for names that need to be qualified with an object name (such as obj.x for the attribute x of object obj).

Python 中所有代码块都有三个作用域,但函数:

There are three scopes in Python for all code blocks, but the functions:

  • 本地
  • 全球
  • 内置

Python 中有四个块仅用于函数(根据PEP 227):

There are four blocks in Python for the functions only (according to PEP 227):

  • 本地
  • 封闭函数
  • 全球
  • 内置

一个变量绑定它并在块中找到它的规则是很简单:

The rule for a variable to bind it to and find it in a block is quite simple:

  • 一个变量与块中对象的任何绑定都会使这个变量局部于这个块,除非变量被声明为全局的(在那个如果变量属于全局范围)
  • 使用规则 LGB (local,全局,内置)适用于所有块,但功能
  • 使用规则 LEGB (local,仅用于函数的封闭、全局、内置).

让我知道举个例子来验证这个规则,并展示许多特别案例.对于每个例子,我都会给出我的理解.请如果我错了,请纠正我.对于最后一个例子,我不明白结果.

Let me know take examples validating this rule, and showing many special cases. For each example, I will give my understanding. Please correct me if I am wrong. For the last example, I don't understand the outcome.

示例 1:

x = "x in module"
class A():
    print "A: "  + x                    #x in module
    x = "x in class A"
    print locals()
    class B():
        print "B: " + x                 #x in module
        x = "x in class B"
        print locals()
        def f(self):
            print "f: " + x             #x in module
            self.x = "self.x in f"
            print x, self.x
            print locals()

>>>A.B().f()
A: x in module
{'x': 'x in class A', '__module__': '__main__'}
B: x in module
{'x': 'x in class B', '__module__': '__main__'}
f: x in module
x in module self.x in f
{'self': <__main__.B instance at 0x00000000026FC9C8>}

类(规则 LGB)和函数没有嵌套作用域一个类不能访问类的属性而不使用限定名称(本例中为 self.x).这在PEP227.

There is no nested scope for the classes (rule LGB) and a function in a class cannot access the attributes of the class without using a qualified name (self.x in this example). This is well described in PEP227.

示例 2:

z = "z in module"
def f():
    z = "z in f()"
    class C():
        z = "z in C"
        def g(self):
            print z
            print C.z
    C().g()
f()
>>> 
z in f()
z in C

这里使用 LEGB 规则查找函数中的变量,但如果一个类在路径中,类参数被跳过.又是在这里,这就是 PEP 227 所解释的.

Here variables in functions are looked up using the LEGB rule, but if a class is in the path, the class arguments are skipped. Here again, this is what PEP 227 is explaining.

示例 3:

var = 0
def func():
    print var
    var = 1
>>> func()

Traceback (most recent call last):
  File "<pyshell#102>", line 1, in <module>
func()
  File "C:/Users/aa/Desktop/test2.py", line 25, in func
print var
UnboundLocalError: local variable 'var' referenced before assignment

我们期望使用诸如 python 之类的动态语言,一切都是动态解决.但对于函数而言,情况并非如此.当地的变量是在编译时确定的.PEP 227 和http://docs.python.org/2.7/reference/executionmodel.html 描述这个这样的行为

We expect with a dynamic language such as python that everything is resolved dynamically. But this is not the case for functions. Local variables are determined at compile time. PEP 227 and http://docs.python.org/2.7/reference/executionmodel.html describe this behavior this way

"如果名称绑定操作发生在代码块内的任何位置,则所有块内名称的使用被视为对当前块."

"If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block."

示例 4:

x = "x in module"
class A():
    print "A: " + x
    x = "x in A"
    print "A: " + x
    print locals()
    del x
    print locals()
    print "A: " + x
>>> 
A: x in module
A: x in A
{'x': 'x in A', '__module__': '__main__'}
{'__module__': '__main__'}
A: x in module

但是我们在这里看到 PEP227 中的这个声明如果一个名称绑定操作发生在代码块内的任何地方,名称的所有使用块内被视为对当前块的引用."是当代码块是一个类时出错.此外,对于类,似乎本地名称绑定不是在编译时进行的,而是在使用类命名空间执行.在这方面,PEP227 和 Python 文档中的执行模型具有误导性,对于有些地方错了.

But we see here that this statement in PEP227 "If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block." is wrong when the code block is a class. Moreover, for classes, it seems that local name binding is not made at compile time, but during execution using the class namespace. In that respect, PEP227 and the execution model in the Python doc is misleading and for some parts wrong.

示例 5:

x = 'x in module'
def  f2():
    x = 'x in f2'
    def myfunc():
        x = 'x in myfunc'
        class MyClass(object):
            x = x
            print x
        return MyClass
    myfunc()
f2()
>>> 
x in module

我对这段代码的理解如下.指令 x = x首先查找表达式右手 x 所指的对象至.在这种情况下,在类中本地查找对象,然后遵循 LGB 规则,它在全局范围内查找,即字符串'x in module'.那么 MyClass 的本地属性 x 是在类字典中创建并指向字符串对象.

my understanding of this code is the following. The instruction x = x first look up the object the right hand x of the expression is referring to. In that case, the object is looked up locally in the class, then following the rule LGB it is looked up in the global scope, which is the string 'x in module'. Then a local attribute x to MyClass is created in the class dictionary and pointed to the string object.

示例 6:

现在这是一个我无法解释的例子.它非常接近示例 5,我只是在更改本地 MyClass从 x 到 y 的属性.

Now here is an example I cannot explain. It is very close to example 5, I am just changing the local MyClass attribute from x to y.

x = 'x in module'
def  f2():
    x = 'x in f2'
    def myfunc():
        x = 'x in myfunc'
        class MyClass(object):
            y = x
            print y
        return MyClass
    myfunc()
f2()
>>>
x in myfunc

为什么在这种情况下 MyClass 中的 x 引用在最里面的功能?

Why in that case the x reference in MyClass is looked up in the innermost function?

推荐答案

总之,例 5 和例 6 的区别在于,在例 5 中,变量 x 也被赋值给了相同的范围,但不在示例 6 中.这引发了可以通过历史原因理解的差异.

In two words, the difference between example 5 and example 6 is that in example 5 the variable x is also assigned to in the same scope, while not in example 6. This triggers a difference that can be understood by historical reasons.

这会引发 UnboundLocalError:

This raises UnboundLocalError:

x = "foo"
def f():
    print x
    x = 5
f()

而不是打印foo".这有点道理,即使一开始看起来很奇怪:函数 f() 在本地定义变量 x,即使它在打印之后,因此对 x 的任何引用 在同一个函数中必须指向那个局部变量.至少它是有道理的,因为如果您错误地在本地重用了全局变量的名称,并且试图同时使用全局变量和局部变量,它可以避免奇怪的惊喜.这是一个好主意,因为这意味着我们可以静态地知道,仅仅通过查看一个变量, 变量意味着什么.例如,我们知道 print x 在这里引用了局部变量(因此可能会引发 UnboundLocalError):

instead of printing "foo". It makes a bit of sense, even if it seems strange at first: the function f() defines the variable x locally, even if it is after the print, and so any reference to x in the same function must be to that local variable. At least it makes sense in that it avoids strange surprizes if you have by mistake reused the name of a global variable locally, and are trying to use both the global and the local variable. This is a good idea because it means that we can statically know, just by looking at a variable, which variable it means. For example, we know that print x refers to the local variable (and thus may raise UnboundLocalError) here:

x = "foo"
def f():
    if some_condition:
        x = 42
    print x
f()

现在,这条规则不适用于类级作用域:在那里,我们希望像 x = x 这样的表达式起作用,将全局变量 x 捕获到类级范围.这意味着类级作用域不遵循上述基本规则:我们无法知道这个作用域中的 x 是指某个外部变量还是本地定义的 x代码> --- 例如:

Now, this rule doesn't work for class-level scopes: there, we want expressions like x = x to work, capturing the global variable x into the class-level scope. This means that class-level scopes don't follow the basic rule above: we can't know if x in this scope refers to some outer variable or to the locally-defined x --- for example:

class X:
    x = x     # we want to read the global x and assign it locally
    bar = x   # but here we want to read the local x of the previous line

class Y:
    if some_condition:
        x = 42
    print x     # may refer to either the local x, or some global x

class Z:
    for i in range(2):
        print x    # prints the global x the 1st time, and 42 the 2nd time
        x = 42

因此在类作用域中,使用了不同的规则:它通常会引发 UnboundLocalError --- 并且仅在这种情况下 --- 它改为在模块全局变量中查找.就是这样:它不遵循嵌套作用域链.

So in class scopes, a different rule is used: where it would normally raise UnboundLocalError --- and only in that case --- it instead looks up in the module globals. That's all: it doesn't follow the chain of nested scopes.

为什么不呢?我实际上怀疑出于历史原因"是否有更好的解释.用更专业的术语来说,它可以认为变量 x 是在类作用域中本地定义的(因为它被分配给了)并且应该从父作用域传入作为词法嵌套变量(因为它被读取).可以通过使用与 LOAD_NAME 不同的字节码来实现它,该字节码在本地范围内查找,如果找不到,则回退到使用嵌套范围的引用.

Why not? I actually doubt there is a better explanation that "for historical reasons". In more technical terms, it could consider that the variable x is both locally defined in the class scope (because it is assigned to) and should be passed in from the parent scope as a lexically nested variable (because it is read). It would be possible to implement it by using a different bytecode than LOAD_NAME that looks up in the local scope, and falls back to using the nested scope's reference if not found.

感谢 wilberforce 对 http://bugs.python.org/issue532860.如果我们认为应该修复建议的新字节码,我们可能有机会重新激活一些讨论(错误报告考虑终止对 x = x 的支持,但由于担心破坏太多现有代码;相反,我在这里建议的是让 x = x 在更多情况下工作).或者我可能错过了另一个好点...

thanks wilberforce for the reference to http://bugs.python.org/issue532860. We may have a chance to get some discussion reactivated with the proposed new bytecode, if we feel that it should be fixed after all (the bug report considers killing support for x = x but was closed for fear of breaking too much existing code; instead what I'm suggesting here would be to make x = x work in more cases). Or I may be missing another fine point...

在当前的 3.4 主干中,CPython 似乎正是这样做的:http://bugs.python.org/issue17853 ... 或不?他们引入字节码的原因略有不同,并且没有系统地使用它......

it seems that CPython did precisely that in the current 3.4 trunk: http://bugs.python.org/issue17853 ... or not? They introduced the bytecode for a slightly different reason and don't use it systematically...

这篇关于Python 中如何解析对变量的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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