164

私はモジュールを作成していて、それが発生させる可能性のある例外の統一された例外階層を持ちたいと考えています (たとえば、すべてのモジュール固有の例外のFooError抽象クラスからの継承)。fooこれにより、モジュールのユーザーはこれらの特定の例外をキャッチし、必要に応じて個別に処理できます。ただし、モジュールから発生する例外の多くは、他の例外が原因で発生します。たとえば、ファイルの OSError が原因でタスクに失敗した場合などです。

私が必要としているのは、キャッチされた例外を「ラップ」して、異なるタイプとメッセージを持つようにすることです。これにより、例外をキャッチしたものによって伝播階層のさらに上に情報が利用できるようになります。しかし、既存の型、メッセージ、およびスタック トレースを失いたくありません。問題をデバッグしようとしている人にとって、これはすべて有用な情報です。トップレベルの例外ハンドラーは役に立ちません。なぜなら、例外が伝播スタックのさらに上に行く前に例外をデコレートしようとしていて、トップレベルのハンドラーでは遅すぎるからです。

fooこれは、モジュールの特定の例外タイプを既存のタイプ (例: ) から派生させることで部分的に解決されclass FooPermissionError(OSError, FooError)ますが、既存の例外インスタンスを新しいタイプでラップしたり、メッセージを変更したりすることは簡単ではありません。

Python のPEP 3134「Exception Chaining and Embedded Tracebacks」では、既存の例外の処理中に新しい例外が発生したことを示すために、Python 3.0 で受け入れられた例外オブジェクトの「連鎖」について説明しています。

私がやろうとしていることは関連しています:以前のPythonバージョンでも動作する必要があり、チェーンではなくポリモーフィズムにのみ必要です。これを行う正しい方法は何ですか?

4

5 に答える 5

259

Python 3では例外連鎖が導入されました ( PEP 3134で説明されています)。これにより、例外を発生させるときに、既存の例外を「原因」として引用できます。

try:
    frobnicate()
except KeyError as exc:
    raise ValueError("Bad grape") from exc

キャッチされた例外 ( exc、KeyError) は、新しい例外である ValueError の一部 (の「原因」) になります。「原因」は、新しい例外をキャッチするコードに利用できます。

この機能を使用して、__cause__属性を設定します。組み込みの例外ハンドラは、トレースバックとともに例外の「原因」と「コンテキスト」</a>を報告する方法も認識しています。


Python 2では、このユースケースには適切な答えがないようです ( Ian BickingNed Batchelderによって説明されているように)。残念。

于 2009-04-27T03:25:42.617 に答える
39

sys.exc_info() を使用してトレースバックを取得し、そのトレースバックで新しい例外を発生させることができます (PEP が言及しているように)。古いタイプとメッセージを保持したい場合は、例外でそれを行うことができますが、それは、例外をキャッチしたものがそれを探す場合にのみ役立ちます。

例えば

import sys

def failure():
    try: 1/0
    except ZeroDivisionError, e:
        type, value, traceback = sys.exc_info()
        raise ValueError, ("You did something wrong!", type, value), traceback

もちろん、これは実際にはそれほど役に立ちません。もしそうなら、その PEP は必要ありません。私はそれをすることをお勧めしません。

于 2009-03-30T05:40:08.753 に答える