python - Python 中如何解析对变量的引用

标签 python variables python-2.7 scope python-internals

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

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

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

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

  • 本地
  • 全局
  • 内置

  • Python 中有四个块仅用于函数(根据
    PEP 227):
  • 本地
  • 封闭函数
  • 全局
  • 内置

  • 将变量绑定(bind)到并在块中找到它的规则是
    非常简单:
  • 任何将变量绑定(bind)到块中的对象都会使该变量成为
    局部于这个块,除非变量被声明为全局的(在那个
    如果变量属于全局范围)
  • 使用规则 LGB (local,
    global, builtin) 用于所有块,但函数
  • 使用规则 LEGB (local,
    仅用于函数的封闭、全局、内置)。

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

    示例 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。

    例子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 所解释的。

    示例 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描述这个
    这样的行为

    “如果名称绑定(bind)操作发生在代码块内的任何地方,所有
    块内名称的使用被视为对
    当前区块。”

    示例 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 中的这条语句“如果名称绑定(bind)
    操作发生在代码块内的任何地方,名称的所有使用
    块内被视为对当前块的引用。”是
    当代码块是一个类时出错。此外,对于类,似乎
    本地名称绑定(bind)不是在编译时进行的,而是在
    使用类命名空间执行。在这方面,
    PEP227 和 Python 文档中的执行模型具有误导性,对于
    有些部分错了。

    示例 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 是
    在类字典中创建并指向字符串对象。

    示例 6:

    现在这是一个我无法解释的例子。
    它非常接近示例 5,我只是在更改本地 MyClass
    从 x 到 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 引用在
    最里面的功能?

    最佳答案

    一句话,例5和例6的区别在于例5中的变量x也被分配到相同的范围内,而在示例 6 中没有。这引发了可以通过历史原因理解的差异。

    这会引发 UnboundLocalError:

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

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

    现在,这条规则不适用于类级作用域:在那里,我们需要像 x = x 这样的表达式。工作,捕获全局变量 x进入类级范围。这意味着类级作用域不遵循上述基本规则:我们不知道 x在此范围内指的是某个外部变量或本地定义的 x - - 例如:
    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 --- 并且仅在这种情况下 --- 它改为在模块全局变量中查找。就是这样:它不遵循嵌套作用域链。

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

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

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

    关于python - Python 中如何解析对变量的引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20246523/

    相关文章:

    javascript - 有没有办法查看当前变量状态等调试方法?

    python - 如何为参数编写自定义解析器

    python - Python 多处理脚本中的打印时间返回负时间

    shell - 从 .zshrc 导出 PYTHONPATH 不起作用

    java - 使用 Eclipse 导入 py4j

    python - 将数据框中的每一列与其右侧的列相乘 Python

    python - python 中的 time.mktime 时区是否免费?

    python - 3D 阵列的 1D 绘图

    jQuery 将 ajax 结果返回到外部变量中

    c - 声明与全局、局部和静态同名的变量