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

有帮助吗?

解决方案

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

这就像我能做到的那样有意义。

道德当然是始终将这些 import 语句放在范围的顶部。

其他提示

哪里出乎意料? 任何变量全局到您在该范围内重新分配的范围,编译器将该范围标记为该范围的本地。

如果以不同方式处理导入,那么 将会令人惊讶。

可能会在不使用符号后命名模块,反之亦然。

嗯,这对我来说很有趣,我可以通过 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'

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

在方法 three()中,因为在本地范围内没有 pprint 名称的声明,所以默认为全局名称 pprint ,这是一个模块

main()中,首先使用关键字 global ,以便所有对 pprint 的引用方法 main()的范围将引用 global name pprint 。我们可以看到最初是一个模块,并且在 global namespace 中使用方法覆盖,因为我们从pprint import pprint <执行 / p>

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

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

编辑另一个有趣的事情。

如果你有一个模块说:

<代码> MOD1

from datetime import    datetime

def foo():
    print "bar"

另一种方法说:

<代码> MOD2

import  datetime
from mod1 import *

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

由于您已在 mod2 中导入模块 datetime ,因此乍一看似乎是正确的。

现在,如果您尝试将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'

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

道德:因此,进口的顺序,进口的性质(来自x import *)和进口模块中的进口意识 - 很重要

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

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模块。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top