14.9 捕获异常后抛出另外的异常

    为了链接异常,使用 语句来代替简单的 raise 语句。它会让你同时保留两个异常的信息。例如:

    上面的异常是下面的异常产生的直接原因:

    1. Traceback (most recent call last):
    2. File "<stdin>", line 1, in <module>
    3. File "<stdin>", line 5, in example
    4. RuntimeError: A parsing error occurred
    5. >>>

    在回溯中可以看到,两个异常都被捕获。要想捕获这样的异常,你可以使用一个简单的 except 语句。不过,你还可以通过查看异常对象的 cause 属性来跟踪异常链。例如:

    当在 块中又有另外的异常被抛出时会导致一个隐藏的异常链的出现。例如:

    1. >>> def example2():
    2. ... try:
    3. ... int('N/A')
    4. ... print("Couldn't parse:", err)
    5. ...
    6. >>>
    7. >>> example2()
    8. Traceback (most recent call last):
    9. File "<stdin>", line 3, in example2
    10. ValueError: invalid literal for int() with base 10: 'N/A'

    这个例子中,你同时获得了两个异常的信息,但是对异常的解释不同。这时候,NameError 异常被作为程序最终异常被抛出,而不是位于解析异常的直接回应中。

    如果,你想忽略掉异常链,可使用 raise from None :

    1. >>> def example3():
    2. ... try:
    3. ... int('N/A')
    4. ... except ValueError:
    5. ...
    6. >>>
    7. example3()
    8. Traceback (most recent call last):
    9. File "<stdin>", line 1, in <module>
    10. File "<stdin>", line 5, in example3
    11. RuntimeError: A parsing error occurred
    12. >>>

    在设计代码时,在另外一个 except 代码块中使用 语句的时候你要特别小心了。大多数情况下,这种 raise 语句都应该被改成 raise from 语句。也就是说你应该使用下面这种形式:

    这样做的原因是你应该显示的将原因链接起来。也就是说,DifferentException 是直接从 衍生而来。这种关系可以从回溯结果中看出来。

    1. try:
    2. ...
    3. except SomeException:

    当你使用 raise from 语句的话,就很清楚的表明抛出的是第二个异常。

    最后一个例子中隐藏异常链信息。尽管隐藏异常链信息不利于回溯,同时它也丢失了很多有用的调试信息。不过万事皆平等,有时候只保留适当的信息也是很有用的。

    原文: