在Python 3中已经存在异常时引发异常

时间:2022-06-06 20:18:23

What happens to my first exception (A) when the second (B) is raised in the following code?

在下面的代码中引发第二个(B)时,我的第一个异常(A)会发生什么?

class A(Exception): pass
class B(Exception): pass

try:
    try:
        raise A('first')
    finally:
        raise B('second')
except X as c:
    print(c)

If run with X = A I get:

如果以X = A运行,我得到:

Traceback (most recent call last):
  File "raising_more_exceptions.py", line 6, in 
    raise A('first')
__main__.A: first

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "raising_more_exceptions.py", line 8, in 
    raise B('second')
__main__.B: second

But if X = B I get:

但如果X = B我得到:

second

Questions

  1. Where did my first exception go?
  2. 我的第一个例外去了哪里?

  3. Why is only the outermost exception catchable?
  4. 为什么只有最外层的例外可以捕获?

  5. How do I peel off the outermost exception and reraise the earlier exceptions?
  6. 如何剥离最外层的异常并重新引用先前的异常?

Update0

This question specifically addresses Python 3, as its exception handling is quite different to Python 2.

这个问题专门针对Python 3,因为它的异常处理与Python 2完全不同。

5 个解决方案

#1


8  

Pythons exception handling will only deal with one exception at a time. However, exception objects are subject to the same variable rules and garbage collection as everything else. Hence, if you save the exception object in a variable somewhere you can deal with it later, even if another exception is raised.

Pythons异常处理一次只处理一个异常。但是,异常对象与其他所有内容一样受到相同的变量规则和垃圾收集的约束。因此,如果将异常对象保存在某个变量中,您可以稍后处理它,即使引发了另一个异常。

In your case, when an exception is raised during the "finally" statement, Python 3 will print out the traceback of the first exception before the one of the second exception, to be more helpful.

在您的情况下,当在“finally”语句中引发异常时,Python 3将在第二个异常之前打印出第一个异常的回溯,以便更有帮助。

A more common case is that you want to raise an exception during an explicit exception handling. Then you can "save" the exception in the next exception. Just pass it in as a parameter:

更常见的情况是您希望在显式异常处理期间引发异常。然后,您可以在下一个异常中“保存”异常。只需将其作为参数传递:

>>> class A(Exception):
...     pass
... 
>>> class B(Exception):
...     pass
... 
>>> try:
...     try:
...         raise A('first')
...     except A as e:
...         raise B('second', e)
... except Exception as c:
...     print(c.args[1])
... 
first

As you see you can now access the original exception.

如您所见,您现在可以访问原始异常。

#2


7  

The 'causing' exception is available as c.__context__ in your last exception handler. Python is using this information to render a more useful traceback. Under Python 2.x the original exception would have been lost, this is for Python 3 only.

'cause'异常在上一个异常处理程序中以c .__ context__的形式提供。 Python正在使用此信息来呈现更有用的回溯。在Python 2.x下,原始异常将丢失,这仅适用于Python 3。

Typically you would use this to throw a consistent exception while still keeping the original exception accessible (although it's pretty cool that it happens automatically from an exception handler, I didn't know that!):

通常你会使用它来抛出一致的异常,同时仍然保持原始异常可访问(尽管从异常处理程序自动发生它非常酷,我不知道!):

try:
    do_something_involving_http()
except (URLError, socket.timeout) as ex:
    raise MyError('Network error') from ex

More info (and some other pretty useful things you can do) here: http://docs.python.org/3.3/library/exceptions.html

这里有更多信息(以及其他一些非常有用的东西):http://docs.python.org/3.3/library/exceptions.html

#3


6  

  1. It got thrown out.
  2. 它被抛弃了。

  3. Only one exception can be "active" at a time per thread.
  4. 每个线程一次只能有一个“活动”异常。

  5. You can't, unless you encapsulate the earlier exception in the later exception somehow.
  6. 你不能,除非你以某种方式将先前的异常封装在后面的异常中。

#4


6  

I believe all the ingredients to answer your question(s) are already in the existing answers. Let me combine and elaborate.

我相信回答你问题的所有要素都已经存在于现有的答案中。让我结合并详细说明。

Let me repeat your question's code to provide line number references:

让我重复一下你的问题代码,提供行号参考:

 1  class A(Exception): pass
 2  class B(Exception): pass
 3 
 4  try:
 5      try:
 6          raise A('first')
 7      finally:
 8          raise B('second')
 9  except X as c:
10      print(c)

So to answer your questions:

所以回答你的问题:

  1. Where did my first exception go?
  2. 我的第一个例外去了哪里?

Your first exception A is raised in line 6. The finally clause in line 7 is always executed as soon as the try block (lines 5-6) is left, regardless if it is left because of successful completion or because of a raised exception. While the finally clause is being executed, line 8 raises another exception B. As Lennart and Ignazio have pointed out, only one exception, the one that is most recently being raised, can be kept track of. So as soon as B is raised, the overall try block (lines 4-8) is quit and the exception B is being caught by the except statement in line 9 if it matches (if X is B).

您的第一个异常A在第6行引发。第7行中的finally子句总是在try块(第5-6行)被保留后立即执行,无论是由于成功完成还是由于引发异常而离开。当finally子句正在执行时,第8行引发了另一个例外B.正如Lennart和Ignazio所指出的那样,只有一个例外,即最近被提出的例外,可以被追踪。因此,只要B被引发,整个try块(第4-8行)就会退出,如果匹配(如果X是B),则第9行中的except语句将捕获异常B.

  1. Why is only the outermost exception catchable?
  2. 为什么只有最外层的例外可以捕获?

Hopefully this is clear now from my explanation of 1. You could catch the inner/lower/first exception, though. To merge in Lennart's answer, slightly modified, here's how to catch both:

希望现在从我对1的解释中可以清楚地看到这一点。但是你可以捕获内/低/第一个异常。要合并Lennart的答案,略微修改,以下是如何捕捉两者:

class A(Exception): pass
class B(Exception): pass
try:
    try:
        raise A('first')
    except A as e:
        raise B('second', e)
except Exception as c:
    print(c)

The output is:

输出是:

('second', A('first',))
  1. How do I peel off the outermost exception and reraise the earlier exceptions?
  2. 如何剥离最外层的异常并重新引用先前的异常?

In Lennart's example the solution to this question is the line except A as e where the inner/lower/first exception is being caught and stored in variable e.

在Lennart的例子中,这个问题的解决方案是除了A之外的行,其中内部/下部/第一个异常被捕获并存储在变量e中。

As a general gut-feeling of when to catch exceptions, when to ignore them, and when to re-raise, maybe this question and Alex Martelli's answer help.

作为对何时捕捉异常,何时忽略它们以及何时重新加注的一般直觉,也许这个问题和Alex Martelli的答案有所帮助。

#5


5  

Answering to question 3, you can use:

回答问题3,您可以使用:

raise B('second') from None

Which will remove the exception A traceback.

这将删除异常A追溯。

Traceback (most recent call last):
  File "raising_more_exceptions.py", line 8, in 
    raise B('second')
__main__.B: second

#1


8  

Pythons exception handling will only deal with one exception at a time. However, exception objects are subject to the same variable rules and garbage collection as everything else. Hence, if you save the exception object in a variable somewhere you can deal with it later, even if another exception is raised.

Pythons异常处理一次只处理一个异常。但是,异常对象与其他所有内容一样受到相同的变量规则和垃圾收集的约束。因此,如果将异常对象保存在某个变量中,您可以稍后处理它,即使引发了另一个异常。

In your case, when an exception is raised during the "finally" statement, Python 3 will print out the traceback of the first exception before the one of the second exception, to be more helpful.

在您的情况下,当在“finally”语句中引发异常时,Python 3将在第二个异常之前打印出第一个异常的回溯,以便更有帮助。

A more common case is that you want to raise an exception during an explicit exception handling. Then you can "save" the exception in the next exception. Just pass it in as a parameter:

更常见的情况是您希望在显式异常处理期间引发异常。然后,您可以在下一个异常中“保存”异常。只需将其作为参数传递:

>>> class A(Exception):
...     pass
... 
>>> class B(Exception):
...     pass
... 
>>> try:
...     try:
...         raise A('first')
...     except A as e:
...         raise B('second', e)
... except Exception as c:
...     print(c.args[1])
... 
first

As you see you can now access the original exception.

如您所见,您现在可以访问原始异常。

#2


7  

The 'causing' exception is available as c.__context__ in your last exception handler. Python is using this information to render a more useful traceback. Under Python 2.x the original exception would have been lost, this is for Python 3 only.

'cause'异常在上一个异常处理程序中以c .__ context__的形式提供。 Python正在使用此信息来呈现更有用的回溯。在Python 2.x下,原始异常将丢失,这仅适用于Python 3。

Typically you would use this to throw a consistent exception while still keeping the original exception accessible (although it's pretty cool that it happens automatically from an exception handler, I didn't know that!):

通常你会使用它来抛出一致的异常,同时仍然保持原始异常可访问(尽管从异常处理程序自动发生它非常酷,我不知道!):

try:
    do_something_involving_http()
except (URLError, socket.timeout) as ex:
    raise MyError('Network error') from ex

More info (and some other pretty useful things you can do) here: http://docs.python.org/3.3/library/exceptions.html

这里有更多信息(以及其他一些非常有用的东西):http://docs.python.org/3.3/library/exceptions.html

#3


6  

  1. It got thrown out.
  2. 它被抛弃了。

  3. Only one exception can be "active" at a time per thread.
  4. 每个线程一次只能有一个“活动”异常。

  5. You can't, unless you encapsulate the earlier exception in the later exception somehow.
  6. 你不能,除非你以某种方式将先前的异常封装在后面的异常中。

#4


6  

I believe all the ingredients to answer your question(s) are already in the existing answers. Let me combine and elaborate.

我相信回答你问题的所有要素都已经存在于现有的答案中。让我结合并详细说明。

Let me repeat your question's code to provide line number references:

让我重复一下你的问题代码,提供行号参考:

 1  class A(Exception): pass
 2  class B(Exception): pass
 3 
 4  try:
 5      try:
 6          raise A('first')
 7      finally:
 8          raise B('second')
 9  except X as c:
10      print(c)

So to answer your questions:

所以回答你的问题:

  1. Where did my first exception go?
  2. 我的第一个例外去了哪里?

Your first exception A is raised in line 6. The finally clause in line 7 is always executed as soon as the try block (lines 5-6) is left, regardless if it is left because of successful completion or because of a raised exception. While the finally clause is being executed, line 8 raises another exception B. As Lennart and Ignazio have pointed out, only one exception, the one that is most recently being raised, can be kept track of. So as soon as B is raised, the overall try block (lines 4-8) is quit and the exception B is being caught by the except statement in line 9 if it matches (if X is B).

您的第一个异常A在第6行引发。第7行中的finally子句总是在try块(第5-6行)被保留后立即执行,无论是由于成功完成还是由于引发异常而离开。当finally子句正在执行时,第8行引发了另一个例外B.正如Lennart和Ignazio所指出的那样,只有一个例外,即最近被提出的例外,可以被追踪。因此,只要B被引发,整个try块(第4-8行)就会退出,如果匹配(如果X是B),则第9行中的except语句将捕获异常B.

  1. Why is only the outermost exception catchable?
  2. 为什么只有最外层的例外可以捕获?

Hopefully this is clear now from my explanation of 1. You could catch the inner/lower/first exception, though. To merge in Lennart's answer, slightly modified, here's how to catch both:

希望现在从我对1的解释中可以清楚地看到这一点。但是你可以捕获内/低/第一个异常。要合并Lennart的答案,略微修改,以下是如何捕捉两者:

class A(Exception): pass
class B(Exception): pass
try:
    try:
        raise A('first')
    except A as e:
        raise B('second', e)
except Exception as c:
    print(c)

The output is:

输出是:

('second', A('first',))
  1. How do I peel off the outermost exception and reraise the earlier exceptions?
  2. 如何剥离最外层的异常并重新引用先前的异常?

In Lennart's example the solution to this question is the line except A as e where the inner/lower/first exception is being caught and stored in variable e.

在Lennart的例子中,这个问题的解决方案是除了A之外的行,其中内部/下部/第一个异常被捕获并存储在变量e中。

As a general gut-feeling of when to catch exceptions, when to ignore them, and when to re-raise, maybe this question and Alex Martelli's answer help.

作为对何时捕捉异常,何时忽略它们以及何时重新加注的一般直觉,也许这个问题和Alex Martelli的答案有所帮助。

#5


5  

Answering to question 3, you can use:

回答问题3,您可以使用:

raise B('second') from None

Which will remove the exception A traceback.

这将删除异常A追溯。

Traceback (most recent call last):
  File "raising_more_exceptions.py", line 8, in 
    raise B('second')
__main__.B: second