【问题标题】:Can I get the local variables of a Python function from which an exception was thrown?我可以获取引发异常的 Python 函数的局部变量吗?
【发布时间】:2012-01-30 04:28:01
【问题描述】:

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

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

try:
    myfunction()
except:
    # can I access v1 from here?

【问题讨论】:

    标签: python exception-handling


    【解决方案1】:

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

    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']
    

    trace 函数的功能以及它所处理的traceback 对象的格式在inspect module documentation 中进行了描述。

    【讨论】:

    • +1 我怀疑这是 OP 想要的,即使有更清洁的方法可以实现他/她的目标。
    • 我认为“正确”的做法是使用 sys.exc_info()。没关系吗?
    • inspect.trace 是解释器堆栈的高级接口。不过,它在幕后使用sys.exc_info。 (实现是return getinnerframes(sys.exc_info()[2], context)。)虽然我不会说任何一种方式都必然不正确,但使用inspect 的功能可能是更好的形式,因为您不必担心实现细节,而且它会让你更清楚你在做什么。
    【解决方案2】:

    可以在frame对象中查找局部变量,可以从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_nexts,具体取决于引发异常的堆栈深度。

    【讨论】:

      【解决方案3】:
      def myFunction()
          v1 = get_a_value()
          raise Exception(v1)
      
      
      try:
          myFunction()
      except Exception, e:
          v1 = e.args[0]
      

      【讨论】:

      • raise Exception(locals()) 如果你想要所有的局部变量
      • @julio.alegria **locals() 可能更方便在异常句柄中访问,如果所有局部变量都需要的话。
      【解决方案4】:

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

      >>> 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
      >>> 
      

      【讨论】:

        【解决方案5】:
        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']
        

        【讨论】:

          【解决方案6】:

          对于不使用raise Exception(some_def_var) 以及不使用诸如“inspect”之类的库的“处理”异常很有用。

          但是,我认为如果要“真正”处理异常,使用raise Exception(some_def_var) 之类的东西可能会更好。 See @capfredf answer.

          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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-07-29
            • 1970-01-01
            • 2022-07-04
            • 1970-01-01
            相关资源
            最近更新 更多