我可以在Python的finally块中获取异常吗?

54 投票
5 回答
51677 浏览
提问于 2025-04-15 15:18

我在我的脚本里有一个 try/finally 的结构。请问在 finally 这个部分能不能获取到具体的错误信息呢?

5 个回答

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()的表现会有所不同,下面的例子就说明了这一点。想了解更多信息,可以参考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())

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

希望这样能让事情变得更清楚一些。

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
85

不,到了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

撰写回答