简体   繁体   English

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

[英]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? 有人可以解释一下为什么它不乐意解决pprintglobals的绑定吗?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM