当前位置: 首页 > 面试题库 >

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

葛成双
2023-03-14
问题内容

消息包含很多示例,时间有点长,但是我希望它能帮助我和其他人更好地掌握Python 2.7中变量和属性查找的全部内容。

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

我将术语变量用于可以不带点号调用的名称,将属性用于需要使用对象名称进行限定的名称(例如obj.x用于对象obj的属性x)。

Python中所有代码块都有三个作用域,但有以下功能:

  • 本地
  • 全球
  • 内建

Python中只有四个功能块(根据PEP 227):

  • 本地
  • 封闭功能
  • 全球
  • 内建

将变量绑定到块中并找到它的规则非常简单:

  • 变量与块中对象的任何绑定都使该变量在此块中成为局部变量,除非将该变量声明为全局变量(在这种情况下,该变量属于全局范围)
  • 对于所有块,使用规则LGB(本地,全局,内置)查找对变量的引用,但函数
  • 仅使用LEGB规则(局部,封闭,全局,内置)查找对变量的引用。

让我知道一些验证此规则的示例,并显示了许多特殊情况。对于每个示例,我都会给出自己的理解。如果我错了,请纠正我。对于最后一个示例,我不了解结果。

范例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以这种方式描述此行为

“如果名称绑定操作发生在代码块内的任何地方,则该块内对该名称的所有使用都将视为对当前块的引用。”

示例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文档中的执行模型具有误导性,并且在某些方面是错误的。

范例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在更多情况下进行工作)。否则我可能会错过另一个要点…

EDIT2:
似乎CPython在当前的3.4主干中确实做到了这一点:http :
//bugs.python.org/issue17853 …
…还是不?他们介绍字节码的原因略有不同,因此没有系统地使用它。



 类似资料:
  • 问题内容: 在代码中: 现在,将是7,x将是8。但是实际上,我想改变。我可以分配引用吗? 例如,在C ++中,可以达到以下目的: 现在,这两个&将是9 问题答案: 你不能。正如其他答案指出的那样,您可以(ab?)使用可变对象的别名来达到类似的效果。但是,这与C ++参考不是同一回事,我想解释一下为避免误解而实际发生的情况。 您会看到,在C ++(和其他语言)中,变量(以及对象字段以及集合中的条目等

  • 我正在创建一个解释器(字节码解释器,也是一个编译器),我发现了一个我无法解决的问题。我需要把变量存储在某个地方。将它们存储在字典中并在运行时查找它们可能会很慢,所以我希望将它们存储在寄存器中,并使用它们的索引而不是名称。 所以在编译时,我给每个变量一个索引,并创建一个寄存器数组。这对于单一范围的语言来说很好。但是我正在为其创建解释器的语言具有嵌套范围(和函数调用)。所以另一种方法可能是我有一组全局

  • 本文向大家介绍解析Python中的变量、引用、拷贝和作用域的问题,包括了解析Python中的变量、引用、拷贝和作用域的问题的使用技巧和注意事项,需要的朋友参考一下 在Python中,变量是没有类型的,这和以往看到的大部分编辑语言都不一样。在使用变量的时候,不需要提前声明,只需要给这个变量赋值即可。但是,当用变量的时候,必须要给这个变量赋值;如果只写一个变量,而没有赋值,那么Python认为这个变量

  • 问题内容: 我想根据用户选择的值导入一些软件包。 默认值为: 如果用户选择,则应为: 在PHP中,我可以使用变量variable来做到这一点: 如何在Python中执行此操作? 问题答案: Python没有与PHP的“变量变量”直接等效的功能。要获取“变量变量”的值(或任何其他表达式的值),可以使用该函数。 但是,这不能在语句中使用。 可以使用该函数通过变量导入。 相当于

  • 本文向大家介绍Python中的全局变量如何理解,包括了Python中的全局变量如何理解的使用技巧和注意事项,需要的朋友参考一下 Python是一种面向对象的开发语言,在函数中使用全局变量,一般应作全局变量说明,只有在函数内经过说明的全局变量才能使用。 首先应该说明的是需要尽量避免使用Python全局变量。不同的模块都可以自由的访问全局变量,可能会导致全局变量的不可预知性。对全局变量,如果程序员甲修

  • 问题内容: Python文档似乎尚不清楚参数是通过引用还是通过值传递,并且以下代码会产生未更改的值“原始” 我可以做些什么来通过实际引用传递变量吗? 问题答案: 传入的参数实际上是对对象的引用(但引用是通过值传递的) 有些数据类型是可变的,但有些则不是 所以: 如果将可变对象传递给方法,则该方法将获得对该对象的引用,并且可以对其进行突变,但是如果您将该引用重新绑定到该方法中,则外部作用域对此一无所