我最近遇到了一个很难追查的错误。我不小心重复使用了一个类名作为变量(见下面的代码),所以当我试图调用类时,我(可以理解)得到了一个错误。跟踪这么难的原因是我的调试器(Wing IDE 5.1.10)会在调试探针中成功执行该行,但是当我试图在解释器中运行相同的行时,它会出错。在进一步调查中,我发现当我使用inspect模块检查帧数据时,名称仍显示为绑定到我的类的全局变量。所以,我在收到一个明确定义并绑定在我的框架中的名称的UnboundLocalError时感到很困惑。
这再现了这个问题:
import inspect
class MyClass(object):
def __init__(self):
print "MyClass init() method called successfully"
def newscope():
#MyClass is not in the current frame's locals:
assert 'MyClass' not in inspect.currentframe().f_locals.keys()
#MyClass is in the current frame's globals and can be called successfully:
class_object = inspect.currentframe().f_globals['MyClass']
print class_object
class_object()
#But, calling MyClass by name results in UnboundLocalError: local
#variable 'MyClass' referenced before assignment:
print MyClass
#Strangely, if at this point I go into the debug probe and run the same
#line (print MyClass) it executes successfully, printing
#"<class '__main__.MyClass'>"
#Re-assigning the name MyClass is what causes the UnboundLocalError:
MyClass = 5
if __name__ == '__main__':
newscope()
结果:
<class '__main__.MyClass'>
MyClass init() method called successfully
Traceback (most recent call last):
Python Shell, prompt 1, line 29
Python Shell, prompt 1, line 19
UnboundLocalError: local variable 'MyClass' referenced before assignment
同样,我理解为什么我得到UnboundLocalError。我不明白的是为什么检查模块仍然显示名称绑定到类对象时,显然不是这种情况。我错过了什么,或者这是检查模块中的错误?
我正在运行python 2.7.11。
答案 0 :(得分:1)
首先,关于异常,我认为你的IDE并不尊重python规范:
范围定义块中名称的可见性。如果在块中定义了局部变量,则其范围包括该块。
[...]
如果名称绑定在块中,则它是该块的局部变量。 如果名称在模块级别绑定,则它是全局变量。 (该 模块代码块的变量是本地的和全局的。)如果是 变量在代码块中使用但在那里没有定义,它是 free 变量强>
[...]
如果根本找不到名称,则会引发NameError异常。如果 名称是指尚未绑定的局部变量,a 引发UnboundLocalError异常。 UnboundLocalError是一个子类 NameError。
https://docs.python.org/2.7/reference/executionmodel.html#naming-and-binding
因此,我理解整个块被解析,它找到你的变量,并且它被添加到局部范围,但在它被赋值之前,它被认为是自由变量 < / p>
修改强>
关于inspect
,我认为它列出了本地命名空间中的绑定变量,因此,您不会看到您的变量。这很合乎逻辑:你会给钥匙&#39; MyClass&#39;如果没有约束呢?
实际上,你应该使用inspect.currentframe().f_code.co_varnames
来获得你想要的东西;)
import inspect
from pprint import pprint
class MyClass(object):
def __init__(self):
print("MyClass init() method called successfully")
def newscope():
pprint(inspect.currentframe().f_code.co_varnames)
print("----------")
pprint(inspect.currentframe().f_locals)
print("----------")
pprint(inspect.currentframe().f_globals)
print("----------")
try:
pprint(MyClass)
except Exception as e:
print(e)
MyClass = 5
pprint(inspect.currentframe().f_locals)
print("----------")
pprint(inspect.currentframe().f_globals)
print("----------")
if __name__ == '__main__':
newscope()
你得到:
('MyClass', 'e')
----------
{}
----------
{'MyClass': <class '__main__.MyClass'>,
'__builtins__': <module 'builtins' (built-in)>,
'__cached__': None,
'__doc__': None,
'__file__': 'test.py',
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f2fa3901160>,
'__name__': '__main__',
'__package__': None,
'__spec__': None,
'inspect': <module 'inspect' from '/usr/lib/python3.5/inspect.py'>,
'newscope': <function newscope at 0x7f2fa39b8f28>,
'pprint': <function pprint at 0x7f2fa1fe66a8>}
----------
local variable 'MyClass' referenced before assignment
{'MyClass': 5}
----------
{'MyClass': <class '__main__.MyClass'>,
'__builtins__': <module 'builtins' (built-in)>,
'__cached__': None,
'__doc__': None,
'__file__': 'test.py',
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f2fa3901160>,
'__name__': '__main__',
'__package__': None,
'__spec__': None,
'inspect': <module 'inspect' from '/usr/lib/python3.5/inspect.py'>,
'newscope': <function newscope at 0x7f2fa39b8f28>,
'pprint': <function pprint at 0x7f2fa1fe66a8>}
----------
删除你的变量
import inspect
from pprint import pprint
class MyClass(object):
def __init__(self):
print("MyClass init() method called successfully")
def newscope():
pprint(inspect.currentframe().f_code.co_varnames)
print("----------")
pprint(inspect.currentframe().f_locals)
print("----------")
pprint(inspect.currentframe().f_globals)
print("----------")
try:
pprint(MyClass)
except Exception as e:
print(e)
# MyClass = 5
pprint(inspect.currentframe().f_locals)
print("----------")
pprint(inspect.currentframe().f_globals)
print("----------")
if __name__ == '__main__':
newscope()
你得到:
('e',)
----------
{}
----------
{'MyClass': <class '__main__.MyClass'>,
'__builtins__': <module 'builtins' (built-in)>,
'__cached__': None,
'__doc__': None,
'__file__': 'test.py',
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fc6d3fcb160>,
'__name__': '__main__',
'__package__': None,
'__spec__': None,
'inspect': <module 'inspect' from '/usr/lib/python3.5/inspect.py'>,
'newscope': <function newscope at 0x7fc6d4082f28>,
'pprint': <function pprint at 0x7fc6d26b06a8>}
----------
<class '__main__.MyClass'>
{}
----------
{'MyClass': <class '__main__.MyClass'>,
'__builtins__': <module 'builtins' (built-in)>,
'__cached__': None,
'__doc__': None,
'__file__': 'test.py',
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fc6d3fcb160>,
'__name__': '__main__',
'__package__': None,
'__spec__': None,
'inspect': <module 'inspect' from '/usr/lib/python3.5/inspect.py'>,
'newscope': <function newscope at 0x7fc6d4082f28>,
'pprint': <function pprint at 0x7fc6d26b06a8>}
----------
答案 1 :(得分:1)
如果为函数中的变量赋值,则该变量将成为该函数中的局部变量。
从创建函数的那一刻起,即在第一次调用函数之前,该变量被视为本地变量。 Python实际上优化了对局部变量的访问,并且没有查找locals()字典,但是“知道”确切地找到每个局部变量的位置(参见this answer about performance within a function)。
所以,这个assignemt在函数结束时完成的事实并没有什么不同。在函数newscope
中,变量MyClass
是一个局部变量。在使用它之后分配给MyClass
变量实际上是导致此示例中UnboundLocalError
的原因。
举一个更简单的例子:
a = 4
def global_a_example():
print a # a is a global variable: prints 4
def local_a_example():
a = 5
print a # a is a local variable: prints 5
def unbound_local_a_example():
print a # a is a local variable, but not initialized: raises UnboundLocalError
a = 5
编辑:解释为什么它看起来像变量绑定
请注意,未绑定的本地人不会在本地dict中结束。那不是因为他们不是当地人。这是因为他们没有约束力。请参阅以下示例:
a = 1
b = 2
def f():
assert 'a' in globals() # of course
assert 'a' not in locals() # local 'a' has not been initialized and has no value
assert 'a' in f.__code__.co_varnames # 'a' is local nevertheless!
assert 'b' not in f.__code__.co_varnames # 'b' is not local...
# a != 1 test would raise and exception here because 'a' is local and uninitialized
a = 10 # initialize local 'a' (and store it in locals)
assert 'a' in globals() # it is still in globals, yes
assert 'a' in locals() # it is also in locals
assert globals()['a'] == 1 # global 'a' has value 1
assert locals()['a'] == 2 # local 'a' has value 2
assert a == 10 # a is local 'a'!
assert b == 2 # b is global 'b'
# but you don't even have to call f()!!!
# 'a' is already defined to be a local variable in f, see:
print f.__code__.co_varnames # prints ('a',)
所以,'a'在写入之前不受约束。它是全局字典中的关键,但这是无关紧要的。它不是从那个dict中使用的,因为它被定义为本地的。