使用检查模块

时间:2016-09-12 18:12:30

标签: python stack-trace

我最近遇到了一个很难追查的错误。我不小心重复使用了一个类名作为变量(见下面的代码),所以当我试图调用类时,我(可以理解)得到了一个错误。跟踪这么难的原因是我的调试器(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。

2 个答案:

答案 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中使用的,因为它被定义为本地的。