我最近碰到了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
被标记为帧的本地。执行模型是否与本地标识符绑定的帧中的 where 没有区别?它可以说,“在这个字节码指令之前引用全局绑定,此时它已经反弹到本地绑定”,或者执行模型是否没有考虑到这一点?
答案 0 :(得分:6)
哪里出乎意料? 任何变量全局到您在该范围内重新分配的范围,编译器将该范围标记为该范围的本地。
如果以不同方式处理导入,那么 将会令人惊讶。
可能会在不使用符号后命名模块,反之亦然。
答案 1 :(得分: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
但未覆盖pprint
中的globals
名称,因为global
关键字不在two()
范围内使用。
在方法three()
中,因为在本地范围内没有声明pprint
名称,所以默认为全局名称pprint
,这是一个模块
而在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 import *)和进口模块中的进口意识 - 很重要。
答案 2 :(得分:4)
在执行任何代码之前,看起来Python看到from pprint import pprint
行并将pprint
标记为main()
的本地名称。由于Python认为pprint应该是一个局部变量,在使用pprint.pprint()
语句“赋值”之前用from..import
引用它,它会抛出该错误。
这就像我能做到的那样有意义。
道德当然是始终将这些import
陈述置于范围的顶端。
答案 3 :(得分:4)
几周前这个问题得到了解答,但我想我可以澄清一下这些答案。首先是一些事实。
1:在Python中,
import foo
几乎与
完全相同foo = __import__("foo", globals(), locals(), [], -1)
2:在函数中执行代码时,如果Python遇到尚未在函数中定义的变量,它将在全局范围内查找。
3:Python对其称为“locals”的函数进行了优化。当Python对函数进行标记时,它会跟踪您分配给的所有变量。它为每个变量分配一个来自本地单调递增整数的数字。当Python运行该函数时,它会创建一个包含与局部变量一样多的插槽的数组,并为每个插槽分配一个特殊值,表示“尚未分配给尚未”,这就是存储这些变量的值的位置。如果引用尚未分配的本地,Python会看到该特殊值并抛出UnboundLocalValue异常。
舞台现已确定。你的“来自pprint import pprint”实际上是一种任务形式。所以Python创建了一个名为“pprint”的局部变量,它封闭了全局变量。然后,当你在函数中引用“pprint.pprint”时,你会遇到特殊值,Python会抛出异常。如果你在函数中没有import语句,那么Python将使用普通的look-in-locals-first-then-look-in-globals解析并在globals中找到pprint模块。
要消除歧义,您可以使用“global”关键字。当然,现在你已经解决了你的问题,我不知道你是否真的需要“全球”或者是否需要其他方法。