我能否在 Python 的 finally 块中获取异常?

54

我的脚本中有一个 try/finally 子句。在 finally 子句中是否可能获取确切的错误消息?

5个回答

84
不,无论是否发生异常,在finally块中sys.exc_info始终为全None。请使用以下方法:
try:
  whatever
except:
  here sys.exc_info is valid
  to re-raise the exception, use a bare `raise`
else:
  here you know there was no exception
finally:
  and here you can do exception-independent finalization

sys.exc_info(在except子句中)是我所需要的。 - Goutham
5
从Python 3.?开始,你可以在finally:块中访问sys.exc_info - Kentzo
1
@Kentzo,我刚刚在Python 3.6中尝试了一下。当你引发异常时,在finally块中sys.exc_info()返回(None, None, None) - Jonathan
7
如果您在其中一个异常处理中进行了处理,那么它们全部都是“None”。请注意,这只是针对代码中的异常处理部分说的。 - Kentzo

17
finally块将会被执行,无论是否抛出异常。正如Josh所指出的那样,你很可能不想在那里处理它。
如果你确实需要已抛出的异常值,那么应该在except块中捕获异常,并适当地处理或重新引发它,然后在finally块中使用该值--并期望在执行过程中没有抛出异常时可能从未被设置。
import sys

exception_name = exception_value = None

try:
    # do stuff
except Exception as e:
    exception_name, exception_value, _ = sys.exc_info()
    raise   # or don't -- it's up to you
finally:
    # do something with exception_name and exception_value
    # but remember that they might still be none

4

实际上,其他答案有点模糊。让我澄清一下。你可以在finally块中随时调用sys.exc_info()。但是,它的输出将取决于异常是否实际上已被引发。

import sys

def f(i):

    try:
        if i == 1:
            raise Exception
    except Exception as e:
        print "except -> " + str(sys.exc_info())
    finally:
        print "finally -> " + str(sys.exc_info())

f(0)
f(1)

>>> 
finally -> (None, None, None)
except -> (<type 'exceptions.Exception'>, Exception(), <traceback object at 0x029438F0>)
finally -> (<type 'exceptions.Exception'>, Exception(), <traceback object at 0x029438F0>)

因此,如果是一级函数,您始终可以在finally块中知道是否发生了异常。但是当调用堆栈长度超过1时,sys.exc_info()的行为将不同,如下面的示例所示。要获取更多信息,请参考How sys.exc_info() works?
import sys

def f(i):

    try:
        if i == 1:
            raise Exception
    except Exception as e:
        print "except -> " + str(sys.exc_info())
    finally:
        print "finally -> " + str(sys.exc_info())

def f1(i):
    if i == 0:
        try:
            raise Exception('abc')
        except Exception as e:
            pass

    f(i)

f1(0)
f1(1)

>>> 
finally -> (<type 'exceptions.Exception'>, Exception('abc',), <traceback object at 0x02A33940>)
except -> (<type 'exceptions.Exception'>, Exception(), <traceback object at 0x02A33990>)
finally -> (<type 'exceptions.Exception'>, Exception(), <traceback object at 0x02A33990>)

我希望这能使事情更加清晰明了。


4
这只适用于Python2。 - coldfix
@coldfix 你的意思是这只适用于Python3吗? - Abhishek Agarwal
4
不,我会在Python2和Python3上执行脚本。 在Python3上(修复了一些语法问题后),f(1)的输出是finally -> (None, None, None),因此你不能轻易地在finally块中获取异常。 - coldfix

0
不,无论是否发生异常,在finally时刻sys.exc_info都是全部为None。请使用[这个]代替:…
另一个回答者是正确的,你应该在except子句中处理这个问题。
然而,为了记录,以下是对原始问题的回答。
import sys
try:
    int("not an integer LOL")
except:
    e = sys.exc_info()[1]
    # isinstance(e, ValueError) == True
    raise # this line is optional; I have it commented for the example output
else:
    e = None # you should do this to avoid a NameError
finally:
    print("I really wanted to access %s inside of a finally clause. And I'm doing so now."
      % repr(e))

这将打印出类似以下的内容:

我真的想在 finally 子句中访问 ValueError("invalid literal for int() with base 10: 'not an integer LOL'")。现在我正在这样做。


-4

1
问题明确说明了提问者想要“从finally子句中获取确切的错误消息”。 - Hugh W

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