web-dev-qa-db-ja.com

Python 3に例外が既に存在する場合に例外を発生させる

次のコードで2番目(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で実行すると、次のようになります:

トレースバック(最後の最後の呼び出し):
ファイル "raising_more_exceptions.py"、6行目
 raise A( 'first')
 __ main __。A:first 
 
上記の例外の処理中に、別の例外が発生しました:
 
トレースバック(最後の最後の呼び出し):
ファイル「raising_more_exceptions.py」、8行目
でB( 'second')を上げる
 __ main __。B:秒

しかし、X = Bの場合:

第二

ご質問

  1. 最初の例外はどこに行きましたか?
  2. なぜ最も外側の例外のみをキャッチできるのですか?
  3. 一番外側の例外を剥がし、以前の例外を再発行するにはどうすればよいですか?

アップデート0

この質問は、例外処理がPython 2とはまったく異なるため、Python 3に特に対処します。

37
Matt Joiner

質問3に回答すると、以下を使用できます。

raise B('second') from None

例外Aトレースバックが削除されます。

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

'causing'例外は、最後の例外ハンドラーで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

9
Daniel

Pythonの例外処理は、一度に1つの例外のみを処理します。ただし、例外オブジェクトには、他のすべてと同じ変数ルールとガベージコレクションが適用されます。したがって、別の例外が発生した場合でも、例外オブジェクトを変数に保存しておけば、後で処理できます。

あなたの場合、「finally」ステートメント中に例外が発生すると、Python 3は、2番目の例外の前に最初の例外のトレースバックを出力します。

より一般的なケースは、明示的な例外処理中に例外を発生させたい場合です。その後、次の例外で例外を「保存」できます。パラメータとして渡すだけです:

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

ご覧のとおり、元の例外にアクセスできます。

8
Lennart Regebro

私はあなたの質問に答えるためのすべての要素が既存の答えに既にあると信じています。組み合わせて詳しく説明します。

行番号の参照を提供するために質問のコードを繰り返しましょう。

 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行目で発生します。7行目のfinally句はalwaysすぐに実行されますtryブロック(5行目から6行目)は、正常に完了したために、または例外が発生したために残されているかどうかに関係なく、残されます。 finally句の実行中に、8行目で別の例外Bが発生します。レナートとイグナツィオが指摘したように、追跡できるのは、最近発生した例外を1つだけです。したがって、Bが発生するとすぐに、全体のtryブロック(行4〜8)が終了し、例外Bexceptによってキャッチされます一致する場合、9行目のステートメント(XBの場合)。

  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. 一番外側の例外を削除し、以前の例外を再発行するにはどうすればよいですか?

Lennartの例では、この質問に対する解決策はexcept A as eここで、内部/下位/最初の例外がキャッチされ、変数eに格納されます。

例外をキャッチするタイミング、無視するタイミング、再発生するタイミングの一般的な直感として、多分 この質問とAlex Martelliの答え ヘルプ。

7
cfi
  1. それは捨てられました。
  2. スレッドごとに一度に「アクティブ」にできる例外は1つだけです。
  3. 以前の例外を何らかの方法で後の例外にカプセル化しない限り、できません。