我能否从抛出异常的Python函数中获取局部变量?

34

我正在为一个项目编写自定义日志系统。如果一个函数抛出异常,我想记录它的本地变量。从捕获异常的except块中访问引发异常的函数的本地变量是否可能?例如:

def myfunction():
    v1 = get_a_value()
    raise Exception()

try:
    myfunction()
except:
    # can I access v1 from here?
6个回答

45

如果您知道异常处理代码需要某个变量的值,那么将该值传递给异常通常是更清晰的设计。但是,如果您正在编写调试器或类似的东西,需要在不提前知道哪些变量的情况下访问变量,您可以访问抛出异常发生时上下文中的任意变量:

def myfunction():
    v1 = get_a_value()
    raise Exception()

try:
    myfunction()
except:
    # can I access v1 from here?
    v1 = inspect.trace()[-1][0].f_locals['v1']

inspect模块文档中,描述了trace函数的功能和它处理的traceback对象的格式。


1
+1 我怀疑这就是 OP 想要的,即使有更简洁的方式来实现他/她的目标。 - Chris Lutz
我认为“正确”的做法是使用sys.exc_info()。这样做不重要吗? - Nathan Binkert
2
inspect.trace是解释器堆栈的高级接口。它在幕后使用sys.exc_info,但实现方式是return getinnerframes(sys.exc_info()[2], context)。虽然我不会说哪种方式一定是错误的,但最好使用inspect的函数,因为你不必担心实现细节,而且这将使你实际正在做什么更清晰明了。 - David Z

7

你可以通过从 sys.exc_info 获取的框架对象查找局部变量。

>>> import sys
>>> def f(a):
...     b = a - 1
...     print 1.0 / b
...
>>> try:
...     f(1)
... except Exception, e:
...     print sys.exc_info()[2].tb_next.tb_frame.f_locals
...
{'a': 1, 'b': 0}

根据异常抛出的堆栈深度,您将需要包含适当数量的tb_next


2
def myFunction()
    v1 = get_a_value()
    raise Exception(v1)


try:
    myFunction()
except Exception, e:
    v1 = e.args[0]

4
如果你想获取所有本地变量,可以使用raise Exception(locals()) - juliomalegria
@julio.alegria 如果所有本地变量都需要,在异常处理中使用locals()可能更方便。 - capfredf

1

是的,如果它是你自己的异常类型。像这样:

>>> class MyExn(Exception):
...     def __init__(self, val):
...             self.val = val
...     def __str__(self):
...             print "something wrong with:", str(self.val)
... 
>>> def foo():
...     val = 42
...     raise MyExn(val)
... 
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in foo
__main__.MyExnsomething wrong with: 42

>>> # Or in a try block:
>>> try:
...     foo()
... except MyExn as e:
...     print e.val
... 
42
>>> 

0
try:
    myfunction()
except:
    import sys
    type, value, tb = sys.exc_info()
    while tb.tb_next:
        tb = tb.tb_next
    frame = tb.tb_frame
    print frame.f_locals['v1']

0

对于“处理”异常而不使用raise Exception(some_def_var),也不使用例如“inspect”之类的库非常有用。

然而,我认为如果要“真正”处理异常,最好使用像raise Exception(some_def_var)这样的东西。 请参见@capfredf的答案

class MyClass():

    def __init__(self):
        self.my_attr_I = ""
        self.my_attr_II = ""

    def my_def_I(self):
        try:
            self.my_def_II()
        except Exception as e:
            print(self.my_attr_I)
            print(self.my_attr_II)

    def my_def_II(self):
        self.my_attr_I = "TO EXCEPT I"
        self.my_attr_II = "TO except II"

        # NOTE: We will treat it as a general exception! By Questor
        zero_division = 1/0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接