Возбуждение исключений, когда исключение уже присутствует в Python 3 - PullRequest
34 голосов
/ 08 июня 2011

Что происходит с моим первым исключением (A), когда второе (B) вызывается в следующем коде?

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

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

Если запустить с 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

Но если X = B, я получу:

second

Вопросы

  1. Куда делось мое первое исключение?
  2. Почему ловится только самое внешнее исключение?
  3. Как мне убрать самое внешнее исключение и восстановить более ранние исключения?

Update0

Этот вопрос конкретно касается Python 3, так как его обработка исключений сильно отличается от Python 2.

Ответы [ 5 ]

14 голосов
/ 23 мая 2017

Отвечая на вопрос 3, вы можете использовать:

raise B('second') from None

Что удалит исключение A traceback.

Traceback (most recent call last):
  File "raising_more_exceptions.py", line 8, in 
    raise B('second')
__main__.B: second
9 голосов
/ 12 февраля 2013

Исключение «вызывающее» доступно как c .__ context__ в вашем последнем обработчике исключений. Python использует эту информацию для более полезной трассировки. В Python 2.x исходное исключение было бы потеряно, это только для Python 3.

Обычно вы используете это, чтобы генерировать непротиворечивое исключение, сохраняя при этом доступность исходного исключения (хотя довольно здорово, что это происходит автоматически из обработчика исключений, я этого не знал!):

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

Больше информации (и некоторых других полезных вещей, которые вы можете сделать) здесь: http://docs.python.org/3.3/library/exceptions.html

8 голосов
/ 08 июня 2011

Обработка исключений Pythons будет обрабатывать только одно исключение за раз. Однако на объекты исключений распространяются те же правила для переменных и сборка мусора, что и на все остальное. Следовательно, если вы сохраняете объект исключения в переменной где-то, вы можете иметь дело с ним позже, даже если возникает другое исключение.

В вашем случае, когда исключение возникает во время оператора "finally", Python 3 распечатает трассировку первого исключения перед вторым исключением, чтобы быть более полезным.

Более распространенным случаем является то, что вы хотите вызвать исключение во время явной обработки исключения. Затем вы можете «сохранить» исключение в следующем исключении. Просто передайте его как параметр:

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

Как видите, теперь вы можете получить доступ к исходному исключению.

7 голосов
/ 05 сентября 2012

Я считаю, что все ингредиенты для ответа на ваш вопрос (ы) уже есть в существующих ответах. Позвольте мне объединить и уточнить.

Позвольте мне повторить код вашего вопроса, чтобы предоставить ссылки на номера строк:

 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)

Итак, чтобы ответить на ваши вопросы:

  1. Куда делось мое первое исключение?

Ваше первое исключение A вызывается в строке 6. Предложение finally в строке 7 - это всегда , выполняемое, как только остается блок try (строки 5-6), независимо от того, если он оставлен из-за успешного завершения или из-за возбужденного исключения. Во время выполнения предложения finally в строке 8 возникает еще одно исключение B. Как указали Леннарт и Игнацио, можно отследить только одно исключение - последнее, о котором недавно говорили. Таким образом, как только значение B повышается, общий блок try (строки 4-8) завершается, и исключение B перехватывается оператором except в строке 9, если оно совпадает (если X) B).

  1. Почему ловится только самое внешнее исключение?

Надеюсь, теперь это ясно из моего объяснения 1. Вы можете поймать внутреннее / нижнее / первое исключение, хотя. Чтобы объединить ответ Леннарта, слегка модифицированный, вот как можно поймать оба:

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)

Вывод:

('second', A('first',))
  1. Как мне убрать крайнее исключение и перевыпустить более ранние исключения?

В примере Леннарта решением этого вопроса является строка except A as e, где внутреннее / нижнее / первое исключение перехватывается и сохраняется в переменной e.

Как общее ощущение того, когда нужно ловить исключения, когда их игнорировать, а когда повторно поднимать, возможно этот вопрос и ответ Алекса Мартелли помогают.

6 голосов
/ 08 июня 2011
  1. Его выбросили.
  2. Только одно исключение может быть "активным" одновременно для каждого потока.
  3. Вы не можете, если как-то не инкапсулируете более раннее исключение в более позднее исключение.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...