重新抛出Python异常。应该捕获哪个?

51

我正在学习使用Python。我刚看到这篇文章:http://nedbatchelder.com/blog/200711/rethrowing_exceptions_in_python.html。它介绍了如何在Python中重新抛出异常,就像这样:

try:
    do_something_dangerous()
except:
    do_something_to_apologize()
    raise

由于您重新抛出了异常,因此应该有一个“外部catch-except”语句。但是现在,我在想,如果except中的do_something_to_apologize()引发错误,那么外部的“catch-except”将捕获哪个异常? 是您重新抛出的异常还是do_something_to_apologize()引发的异常?或者优先级最高的异常会首先被捕获?


先发生的。 - Ismail Badawi
你正在捕捉的那个? - Bosiwow
1
这正是为什么你想要编写异常处理程序,它们要么不能引发异常,要么只能在你非常了解的特定情况下引发异常,而且你至少在早期调试期间想要实际获取异常对象,这样你就可以例如 print('do_something_dangerous raised {!r}'.format(e)) 至少查看并了解正在发生什么。 - abarnert
1
此外,请注意在 Ned 的“真实”代码中,他仅处理 except Exception,而不是裸的 except:。部分原因是为了让他能够使用 except Exception, e: 捕获异常(尽管这是一个旧博客,所以它使用旧语法;你应该使用 except Exception as e:),另一部分原因是避免捕获像 KeyboardInterrupt 这样的东西,你很少想要处理它们。(当你需要处理时,请使用 except BaseException: 显式地处理。在旧版本的 Python 中,这不是一个选项,但假设你正在编写例如 2.6 或 3.0 的版本,那么它就是。) - abarnert
1
针对这种情况,您可能希望以某种方式处理“apologize”代码,以便您可以引发异常,让您的代码/最终用户知道您失败了,然后无法清理。 (或者在目标驱动器/文件系统的临时位置创建文件夹,以便最坏的情况不会那么糟糕,也可能不必报告。)但除此之外,是的,这是一个完全合理的使用异常处理代码的例子。 - abarnert
显示剩余2条评论
3个回答

85

试一试看:

def failure():
    raise ValueError, "Real error"

def apologize():
    raise TypeError, "Apology error"

try:
    failure()
except ValueError:
    apologize()
    raise

结果:

Traceback (most recent call last):
  File "<pyshell#14>", line 10, in <module>
    apologize()
  File "<pyshell#14>", line 5, in apologize
    raise TypeError, "Apology error"
TypeError: Apology error

原因:原始函数中的“真实”错误已被except捕获。 apologize在达到raise之前引发了一个新错误,因此except子句中的raise从未执行,只有道歉的错误向上传播。如果apologize引发错误,则Python无法知道您将在apologize后引发不同的异常。

请注意,在Python 3中,回溯将提到两个异常,并解释第二个异常是如何产生的:

Traceback (most recent call last):
  File "./prog.py", line 9, in <module>
  File "./prog.py", line 2, in failure
ValueError: Real error

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./prog.py", line 11, in <module>
  File "./prog.py", line 5, in apologize
TypeError: Apology error

然而,第二个异常(“道歉”异常)仍然是唯一一个向外传播并可以被更高级别的 except 子句捕获的异常。原始异常在回溯中提到,但被后面的异常所包含,并且不能再次被捕获。


1
有没有办法在Python 2中实现Python 3的行为? - Binyamin

8

do_something_to_apologize() 抛出的异常将被捕获。由于 do_something_to_apologize() 抛出了异常,包含 raise 的代码行将永远不会被执行。另外,在 Python 异常中,我认为没有任何“优先级”的概念。


似乎其他答案得到了最多的投票,可能是因为它详细并采用了“试一试”的方法(总是很好),但在我看来,它实际上没有回答原始问题,即“哪一个将被捕获在外部的'catch-except'中?你重新抛出的那个还是do_something_to_apologize()抛出的那个?” ibebrett的这个答案回答了这个问题。通常规则是,一旦某些东西引发异常,直到第一个异常被捕获之前,没有其他东西也可以引发异常:因此,如果apologize()引发异常,则紧随其后的raise将永远不会被执行。 - Daniel Goldfarb

3
我认为更好的想法是使用
raise NewException("Explain why") from CatchedException

正则表达式。特别地,考虑到 Python 3 和由 @BrenBarn 给出的示例,我使用以下内容:

def failure():
    raise ValueError("Real error")

try:
    failure()
except ValueError as ex:
    raise TypeError("Apology error") from ex

产生的结果为
--------- ValueError----                                
Traceback (most recent call last) 
      4 try:
----> 5     failure()
      6 except ValueError as ex:

      1 def failure():
----> 2     raise ValueError("Real error")
      3 

ValueError: Real error

The above exception was the direct cause of the following exception:

-----TypeError-----
Traceback (most recent call last) 
      5     failure()
      6 except ValueError as ex:
----> 7     raise TypeError("Apology error") from ex

TypeError: Apology error

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