8

我最近遇到了这种情况UnboundLocalError,这似乎很奇怪:

import pprint

def main():
    if 'pprint' in globals(): print 'pprint is in globals()'
    pprint.pprint('Spam')
    from pprint import pprint
    pprint('Eggs')

if __name__ == '__main__': main()

产生:

pprint is in globals()
Traceback (most recent call last):
  File "weird.py", line 9, in <module>
    if __name__ == '__main__': main()
  File "weird.py", line 5, in main
    pprint.pprint('Spam')
UnboundLocalError: local variable 'pprint' referenced before assignment

pprint明确地绑定在 中globals,并且将locals在以下语句中绑定。有人可以解释为什么它不乐意解决这里pprint的绑定globals吗?

编辑:感谢良好的回应,我可以用相关术语澄清我的问题:

在编译时,标识符pprint被标记为框架的本地。执行模型在框架内绑定本地标识符的位置是否没有区别?是否可以说“在此字节码指令之前引用全局绑定,此时它已被重新绑定到本地绑定”,或者执行模型是否没有考虑到这一点?

4

4 回答 4

6

惊喜在哪里?您在该范围内重新分配的范围内的任何全局变量都被编译器标记为该范围内的本地变量。

如果进口的处理方式不同,我直言。

但是,它可能会在其中使用的符号之后不命名模块,反之亦然。

于 2009-01-01T09:42:23.197 回答
5

好吧,这对我来说很有趣,我做了一些实验,我通读了http://docs.python.org/reference/executionmodel.html

然后在这里和那里修改你的代码,这就是我能找到的:

代码:

import pprint

def two():
    from pprint import pprint
    print globals()['pprint']
    pprint('Eggs')
    print globals()['pprint']

def main():
    if 'pprint' in globals():
        print 'pprint is in globals()'
    global  pprint
    print globals()['pprint']
    pprint.pprint('Spam')
    from pprint import pprint
    print globals()['pprint']
    pprint('Eggs')

def three():
    print globals()['pprint']
    pprint.pprint('Spam')

if __name__ == '__main__':
    two()
    print('\n')
    three()
    print('\n')
    main()

输出:

<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Eggs'
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>

<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'

pprint is in globals()
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'
<function pprint at 0xb7d596f4>
'Eggs'

在方法two() from pprint import pprint中但不覆盖中的名称pprintglobals因为global关键字在 的范围内使用two()

在方法three()中,由于在本地范围内没有pprint名称声明,因此默认为全局名称pprint,即模块

而在 in 中main(),首先使用关键字global pprint因此方法范围内的所有引用都main()将引用该global名称pprint。正如我们所看到的,它最初是一个模块,并且在global namespace我们执行的方法中被覆盖from pprint import pprint

虽然这可能无法回答这个问题,但我认为这是一些有趣的事实。

======================

编辑另一个有趣的事情。

如果你有一个模块说:

mod1

from datetime import    datetime

def foo():
    print "bar"

另一种方法说:

mod2

import  datetime
from mod1 import *

if __name__ == '__main__':
    print datetime.datetime.now()

乍一看似乎是正确的,因为您已将模块导入datetime.mod2

现在,如果您尝试将 mod2 作为脚本运行,它将引发错误:

Traceback (most recent call last):
  File "mod2.py", line 5, in <module>
    print datetime.datetime.now()
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'

因为第二个导入from mod2 import * 覆盖了datetime命名空间中的名称,因此第一个import datetime不再有效。

道德:因此,导入的顺序、导入的性质(从 x 导入 *)和导入模块中的导入意识 -很重要

于 2009-01-01T08:21:10.003 回答
4

看起来 Python在执行任何代码之前from pprint import pprint会看到该行并将其标记pprint为本地名称。由于 Python 认为 pprint 应该是一个局部变量,在使用语句“分配”它之前引用它,它会抛出该错误。 main() pprint.pprint()from..import

这是我所能理解的。

当然,道德是始终将这些import陈述放在范围的顶部。

于 2009-01-01T06:02:48.583 回答
4

这个问题在几周前得到了回答,但我想我可以稍微澄清一下答案。首先是一些事实。

1:在 Python 中,

import foo

几乎完全一样

foo = __import__("foo", globals(), locals(), [], -1)

2:在函数中执行代码时,如果 Python 遇到一个尚未在函数中定义的变量,它会在全局范围内查找。

3:Python 对名为“locals”的函数进行了优化。当 Python 标记一个函数时,它会跟踪您分配给的所有变量。它为这些变量中的每一个分配一个来自局部单调递增整数的数字。当 Python 运行该函数时,它会创建一个包含与局部变量一样多的插槽的数组,并为每个插槽分配一个特殊值,表示“尚未分配”,这就是存储这些变量的值的地方。如果您引用尚未分配给的本地,Python 会看到该特殊值并引发 UnboundLocalValue 异常。

现在舞台已经搭建好了。您的“从 pprint 导入 pprint”实际上是一种分配形式。因此 Python 创建了一个名为“pprint”的局部变量,它会遮挡全局变量。然后,当您在函数中引用“pprint.pprint”时,您点击了特殊值,Python 会抛出异常。如果你在函数中没有那个 import 语句,Python 会使用普通的look-in-locals-first-then-look-in-globals 解析并在 globals 中找到 pprint 模块。

为了消除歧义,您可以使用“global”关键字。当然,现在你已经解决了你的问题,我不知道你是否真的需要“全局”或者是否需要其他方法。

于 2010-01-27T11:25:45.670 回答