【问题标题】:"local variable referenced before assignment" — only functions?“赋值前引用的局部变量”——只有函数?
【发布时间】:2012-01-31 23:33:41
【问题描述】:

取以下代码:

import something

def Foo():
    something = something.SomeClass()
    return something

...这显然不是有效的代码:

UnboundLocalError: local variable 'something' referenced before assignment

...因为局部变量 something 在评估 = 的 RHS 之前已创建但未分配。 (例如,请参阅this related answer's comment。)这对我来说似乎有点奇怪,但可以肯定的是,我会接受的。现在,为什么下面的代码是有效的?

class Foo(object):
    something = something.SomeClass()

我的理解是 class 定义的内部本质上是一个范围:

然后使用新创建的本地命名空间和原始全局命名空间在新的执行框架中执行该类的套件(请参阅命名和绑定部分)。

那么,为什么该代码的行为与函数的行为不同?

【问题讨论】:

  • something 好像不止一个意思?
  • @Johnsyweb:是的,有点。但在这两种情况下,它都具有相同的多于一个含义。 (或者,至少,我读过的所有文档似乎都这么说。)

标签: python scope


【解决方案1】:

来自python class documentation

类定义在本地范围内放置了另一个命名空间。

Python 的一个特殊之处在于——如果没有全局语句生效——对名称的赋值总是进入最内部的作用域。赋值不会复制数据——它们只是将名称绑定到对象。删除也是如此:语句 del x 从本地作用域引用的命名空间中删除 x 的绑定。事实上,所有引入新名称的操作都使用本地范围:特别是导入语句和函数定义将模块或函数名称绑定在本地范围内。 (全局语句可用于指示特定变量位于全局范围内。)

因此,在函数(或范围)内,赋值创建了一个本地未绑定变量,在绑定之前访问该变量,而在类定义中,它在赋值时在该类的“命名空间”字典中创建一个条目,允许将something 解析为外部命名空间(模块命名空间)。

【讨论】:

    【解决方案2】:

    考虑以下可能有助于澄清这一点的示例:

    import datetime
    
    class Foo(object):
        datetime = datetime.datetime
    
    >>> datetime
    <module 'datetime' from '/usr/lib/python2.6/lib-dynload/datetime.so'>
    >>> Foo.datetime
    <type 'datetime.datetime'>
    

    请注意,datetime = datetime.datetime 行实际上是分配给名称 Foo.datetime,这与全局 datetime 没有歧义(就像函数中包含相同的代码一样)。

    总而言之,因为类定义创建了一个新的命名空间以及一个新的作用域,所以您可以直接访问封闭作用域中的名称并分配给本地作用域中的相同名称。

    【讨论】:

    • "请注意,datetime = datetime.datetime 行实际上分配给名称 Foo.datetime,这与全局 datetime 并没有歧义" — 否:它分配给名称 datetime:如果您添加两个print datetime 语句,一个在datetime = 之前,一个在之后,那么该语句中的datetime 如何 模棱两可,在某种程度上不同于相同的语句一个函数?
    • @Thanatos - 是的,我可以看出这是如何不清楚的。我想说的是,类创建的命名空间允许以明确的方式从其他范围访问全局属性和类属性,但你是对的,在类的本地范围内仍然存在歧义。
    • 但问题的关键是这与函数中发生的情况有何不同?你说,“因为类定义创建了一个新的命名空间和一个新的范围”——函数不是同时创建一个新的命名空间和一个新的范围吗? — “您可以直接访问封闭范围内的名称” — 我可以在函数中执行此操作 — “并在本地范围内分配相同的名称” — 我只能在类定义中执行此操作……为什么?
    • "函数不创建一个新的命名空间和一个新的范围吗?" — 当函数返回或引发异常时,函数创建的命名空间被删除,而类创建的命名空间在类定义之后仍然存在。命名空间行为的这种差异是允许在类而不是函数中进行分配的基础。
    猜你喜欢
    • 1970-01-01
    • 2013-08-02
    • 2011-11-06
    • 2018-01-12
    • 1970-01-01
    相关资源
    最近更新 更多