Python全局变量,本地变量和UnboundLocalError

时间:2009-01-01 04:48:54

标签: python binding scope identifier

我最近碰到了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 没有区别?它可以说,“在这个字节码指令之前引用全局绑定,此时它已经反弹到本地绑定”,或者执行模型是否没有考虑到这一点?

4 个答案:

答案 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”关键字。当然,现在你已经解决了你的问题,我不知道你是否真的需要“全球”或者是否需要其他方法。