在全局命名空间中查找类定义中未绑定的局部变量-这是什么意思?

时间:2019-06-13 12:17:30

标签: python scoping

https://docs.python.org/3/reference/executionmodel.html#resolution-of-names的最后一段说

  

在名称解析的上下文中,类定义块和exec()和eval()的参数是特殊的。类定义是可以使用和定义名称的可执行语句。这些引用遵循通常的名称解析规则,但在全局命名空间中查找未绑定的局部变量。

引用文字的最后一句是什么意思?首先,我从中推断出以下代码将打印1

a = 1

def foo():
    a = 2
    def bar():
        class Bar:
            b = a
        print(Bar.b)
    bar()

foo()

但是我错了-由上面的代码组成的模块,在运行时会打印2,即类定义中的名称a,即使它未绑定在类定义块中,与文档所说的相反,它没有绑定在本地块的外部,也没有在全局名称空间中查找。

我尝试了下面描述的另一个代码段(使用del语句,该语句是将变量绑定到其中的结构)

a = 1

def foo():
    a = 2
    def bar():
        class Bar:
            del a
        print(Bar.b)
    bar()

foo()

但是del语句引发NameError: name 'a' is not defined

所以,我不明白,那句话是什么意思?

3 个答案:

答案 0 :(得分:6)

According to the docs

  

如果名称绑定在块中,则除非声明为非本地或全局变量,否则它是该块的局部变量。

在您的第一个代码块中,a没有绑定到class Bar定义中的任何内容,因此它不是该块的局部变量。

绑定名称的一种方法是在赋值语句的左侧使用它。这是一个例子。

a = 1
def foo():
    a = 2
    class Bar:
        b = a
        a = 3
    print(Bar.b)
foo()

结果:

1

这说明了“在全局名称空间中查找未绑定局部变量”的原理-b = a使用全局a的值,而不是局部区域a的值到foo


在第二个示例中,a 被认为是class Bar块的本地对象,因为“在del语句中出现的目标也被认为是绑定的”确定名称范围的目的。但是“在全局名称空间中查找未绑定的局部变量”并不重要,因为del不需要查找名称的值即可解除绑定。

出于良好的考虑,我们可以通过实验确认del语句向解释器发送信号,表明应将名称视为本地名称。

a = 1
def foo():
    a = 2
    class Bar:
        print(a)
        del a
foo()

结果:

1
Traceback (most recent call last):
  File "C:\Users\Kevin\Desktop\test.py", line 7, in <module>
    foo()
  File "C:\Users\Kevin\Desktop\test.py", line 4, in foo
    class Bar:
  File "C:\Users\Kevin\Desktop\test.py", line 6, in Bar
    del a
NameError: name 'a' is not defined

在这里我们看到print(a)成功地查询了局部变量a的值,然后在下一行它崩溃了,因为del无法删除未绑定的局部变量。

答案 1 :(得分:1)

范围的局部变量是该范围中任何位置定义的任何名称。值得注意的是,变量本身是局部变量,而不是分配给它的值-局部变量可以在赋值之前或与赋值无关。

>>> def foo():  # new function scope
...     a = 3    # local variable `a`
...     b: int   # local variable `b`
...     c = 3    # local variable `c`
...     del c
...     print(x)
...     x = 3    # local variable `x`
... foo()
UnboundLocalError: local variable 'x' referenced before assignment

未绑定局部变量就是这样的局部变量,未绑定任何值。在上面的示例中,bcx中的所有一个都未绑定。

您的示例中的

都不访问未绑定的局部变量。两者都查找名称a,但从不为其分配名称。


功能块中,引用未绑定的局部变量是错误,即UnboundLocalError。该名称是否也包含在封闭范围内都没关系。

>>> x = 1
>>> def foo():
...     b = x  # local variable is looked up locally
...     x = 2  # make `x` a local variable
... foo()
UnboundLocalError: local variable 'x' referenced before assignment

class块中,引用未绑定的局部变量将回退到全局范围中的查找。这可能成功也可能不会成功。

>>> x = 1
>>> class Foo:
...     b = x  # local variable is looked up locally *or globally*
...     x = 2  # make `x` a local variable
... print(Foo.b, Foo.x)
1 2
>>> class Foo:
...     b = y  # local variable is looked up locally *or globally*
...     y = 2  # make `y` a local variable
... print(Foo.b, Foo.y)
NameError: name 'y' is not defined

答案 2 :(得分:0)

我想我可以做个补充。

  

Python在执行函数主体之前会预先计算包含每个名称的框架。

这意味着这种现象:

In [1]: a = 1

In [2]: def test():
   ...:     print(a)
   ...:

In [3]: test()
1

In [4]: def test():
   ...:     print(a)
   ...:     a = 1
   ...:

In [5]: test()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-5-fbd55f77ab7c> in <module>
----> 1 test()

<ipython-input-4-a08051373573> in test()
      1 def test():
----> 2     print(a)
      3     a = 1
      4

UnboundLocalError: local variable 'a' referenced before assignment

In [6]:

错误local variable 'a' referenced before assignment表示Python预计算函数test的框架具有一个名为a的局部变量,我们必须首先将一个对象分配给a,然后对其进行引用以后。