[英]Python globals, locals, and UnboundLocalError
I ran across this case of UnboundLocalError
recently, which seems strange: 我最近遇到了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()
Which produces: 哪个产生:
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
is clearly bound in globals
, and is going to be bound in locals
in the following statement. pprint
明确地绑定在globals
,并且将在以下语句中绑定到locals
globals
。 Can someone offer an explanation of why it isn't happy resolving pprint
to the binding in globals
here? 有人可以解释一下为什么它不乐意解决pprint
到globals
的绑定吗?
Edit: Thanks to the good responses I can clarify my question with relevant terminology: 编辑:由于回复良好,我可以用相关术语澄清我的问题:
At compile time the identifier pprint
is marked as local to the frame. 在编译时,标识符pprint
被标记为帧的本地。 Does the execution model have no distinction where within the frame the local identifier is bound? 执行模型是否区分本地标识符绑定在框架内的哪个位置 ? Can it say, "refer to the global binding up until this bytecode instruction, at which point it has been rebound to a local binding," or does the execution model not account for this? 它可以说,“引用全局绑定直到这个字节码指令,此时它已经反弹到本地绑定”,或者执行模型没有考虑到这一点?
Where's the surprise? 哪里出乎意料? Any variable global to a scope that you reassign within that scope is marked local to that scope by the compiler. 您在该范围内重新分配的范围的任何全局变量都由编译器标记为该范围的本地。
If imports would be handled differently, that would be surprising imho. 如果进口的处理方式不同, 那将是令人惊讶的。
It may make a case for not naming modules after symbols used therein, or vice versa, though. 但是,可以在不在其中使用符号之后命名模块,反之亦然。
Well, that was interesting enough for me to experiment a bit and I read through http://docs.python.org/reference/executionmodel.html 好吧,这对我来说很有趣,我可以通过http://docs.python.org/reference/executionmodel.html阅读。
Then did some tinkering with your code here and there, this is what i could find: 然后在这里和那里做了一些修补你的代码,这是我能找到的:
code: 码:
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()
output: 输出:
<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'
In the method two()
from pprint import pprint
but does not override the name pprint
in globals
, since the global
keyword is not used in the scope of two()
. 在from pprint import pprint
的方法two()
from pprint import pprint
但是没有覆盖globals
的名称pprint
,因为global
关键字不在 two()
的范围内使用。
In method three()
since there is no declaration of pprint
name in local scope it defaults to the global name pprint
which is a module 在方法three()
因为在本地范围内没有pprint
名称的声明, pprint
它默认为全局名称pprint
,这是一个模块
Whereas in main()
, at first the keyword global
is used so all references to pprint
in the scope of method main()
will refer to the global
name pprint
. 而在main()
,首先使用关键字global
,因此在方法main()
范围内对pprint
所有引用都将引用global
名称pprint
。 Which as we can see is a module at first and is overriden in the global
namespace
with a method as we do the from pprint import pprint
我们可以看到最初是一个模块,并在global
namespace
使用方法覆盖,因为我们from pprint import pprint
执行此from pprint import pprint
Though this may not be answering the question as such, but nevertheless its some interesting fact I think. 虽然这可能没有回答这个问题,但我认为这是一个有趣的事实。
===================== =====================
Edit Another interesting thing. 编辑另一件有趣的事。
If you have a module say: 如果你有一个模块说:
mod1
from datetime import datetime
def foo():
print "bar"
and another method say: 另一种方法说:
mod2
import datetime
from mod1 import *
if __name__ == '__main__':
print datetime.datetime.now()
which at first sight is seemingly correct since you have imported the module datetime
in mod2
. 乍一看似乎是正确的,因为你已经在mod2
导入了模块datetime
时间。
now if you try to run mod2 as a script it will throw an error: 现在,如果您尝试将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'
because the second import from mod2 import *
has overriden the name datetime
in the namespace, hence the first import datetime
is not valid anymore. 因为from mod2 import *
的第二次导入覆盖了命名空间中的名称datetime
,因此第一个import datetime
时间不再有效。
Moral: Thus the order of imports, the nature of imports (from x import *) and the awareness of imports within imported modules - matters . 道德:因此,进口的顺序,进口的性质(来自x进口*)和进口模块中的进口意识 - 很重要 。
This question got answered several weeks ago, but I think I can clarify the answers a little. 几周前这个问题得到了回答,但我想我可以澄清一下这些答案。 First some facts. 首先是一些事实。
1: In Python, 1:在Python中,
import foo
is almost exactly the same as 几乎完全一样
foo = __import__("foo", globals(), locals(), [], -1)
2: When executing code in a function, if Python encounters a variable that hasn't been defined in the function yet, it looks in the global scope. 2:在函数中执行代码时,如果Python遇到尚未在函数中定义的变量,它将在全局范围内查找。
3: Python has an optimization it uses for functions called "locals". 3:Python有一个用于称为“本地”的函数的优化。 When Python tokenizes a function, it keeps track of all the variables you assign to. 当Python对函数进行标记时,它会跟踪您分配给的所有变量。 It assigns each of these variables a number from a local monotonically increasing integer. 它为每个变量分配一个来自本地单调递增整数的数字。 When Python runs the function, it creates an array with as many slots as there are local variables, and it assigns each slot a special value that means "has not been assigned to yet", and that's where the values for those variables are stored. 当Python运行该函数时,它会创建一个包含与局部变量一样多的插槽的数组,并为每个插槽分配一个特殊值,表示“尚未分配给尚未”,这就是存储这些变量的值的位置。 If you reference a local that hasn't been assigned to yet, Python sees that special value and throws an UnboundLocalValue exception. 如果引用尚未分配的本地,Python会看到该特殊值并抛出UnboundLocalValue异常。
The stage is now set. 舞台现已确定。 Your "from pprint import pprint" is really a form of assignment. 你的“来自pprint import pprint”实际上是一种任务形式。 So Python creates a local variable called "pprint" which occludes the global variable. 所以Python创建了一个名为“pprint”的局部变量,它封闭了全局变量。 Then, when you refer to "pprint.pprint" in the function, you hit the special value and Python throws the exception. 然后,当你在函数中引用“pprint.pprint”时,你会遇到特殊值,Python会抛出异常。 If you didn't have that import statement in the function, Python would use the normal look-in-locals-first-then-look-in-globals resolution and find the pprint module in globals. 如果你在函数中没有import语句,那么Python将使用普通的look-in-locals-first-then-look-in-globals解析并在globals中找到pprint模块。
To disambiguate this you can use the "global" keyword. 要消除歧义,您可以使用“global”关键字。 Of course by now you've already worked past your problem, and I don't know whether you really needed "global" or if some other approach was called for. 当然,现在你已经解决了你的问题,我不知道你是否真的需要“全球”或者是否需要其他方法。
Looks like Python sees the from pprint import pprint
line and marks pprint
as a name local to main()
before executing any code. 看起来像Python中看到from pprint import pprint
线和标志pprint
当地的名main()
执行任何代码之前 。 Since Python thinks pprint ought to be a local variable, referencing it with pprint.pprint()
before "assigning" it with the from..import
statement, it throws that error. 由于Python认为pprint应该是一个局部变量,在使用from..import
语句“赋值”之前用pprint.pprint()
引用它,它会抛出该错误。
That's as much sense as I can make of that. 这就像我能做到的那样有意义。
The moral, of course, is to always put those import
statements at the top of the scope. 当然,道德是将这些import
声明放在最重要的范围内。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.