1. ホーム
  2. python

[解決済み] Pythonで例外を手動で発生(スロー)させる

2022-03-14 06:08:16

質問

Pythonで例外を発生させ、その例外を後で except ブロックは?

どのように解決するのですか?

<ブロッククオート

Pythonで例外を手動で投げる/発生させる方法は?

あなたの問題にセマンティックにフィットする最も具体的なExceptionコンストラクタを使用します。 .

メッセージは具体的に、例えば以下のように。

raise ValueError('A very specific bad thing happened.')

汎用的な例外を発生させない

を発生させないようにします。 Exception . この例外をキャッチするためには、この例外をサブクラスとする他のより具体的な例外をすべてキャッチしなければならなくなります。

問題点1:バグの隠蔽

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

例えば

def demo_bad_catch():
    try:
        raise ValueError('Represents a hidden bug, do not catch this')
        raise Exception('This is the exception you expect to handle')
    except Exception as error:
        print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

問題2: キャッチできない

また、より具体的なキャッチは、一般的な例外をキャッチしません。

def demo_no_catch():
    try:
        raise Exception('general exceptions not caught by specific handling')
    except ValueError as e:
        print('we will not catch exception: Exception')
 

>>> demo_no_catch()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

ベストプラクティス raise ステートメント

その代わりに、あなたの問題にセマンティックに適合する最も具体的なExceptionコンストラクタを使用します。 .

raise ValueError('A very specific bad thing happened')

また、コンストラクタに渡す引数の数を任意に設定できるのも便利です。

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

これらの引数へのアクセスは args 属性は Exception オブジェクトを作成します。例えば

try:
    some_code_that_may_raise_our_value_error()
except ValueError as err:
    print(err.args)

印刷物

('message', 'foo', 'bar', 'baz')    

Python 2.5では、実際の message 属性が追加され BaseException を使わないで、Exceptionをサブクラス化することを推奨しています。 args しかし の導入は message と、当初非推奨だったargsが撤回されました。 .

ベストプラクティス except 条項

except節内では、例えば、特定のタイプのエラーが発生したことを記録し、その後、再レイズしたい場合があります。スタックトレースを維持しながらこれを行う最良の方法は、素の raise 文を使用することです。例えば

logger = logging.getLogger(__name__)

try:
    do_something_in_app_that_breaks_easily()
except AppError as error:
    logger.error(error)
    raise                 # just this!
    # raise AppError      # Don't do this, you'll lose the stack trace!

エラーを修正しないで...どうしてもと言うなら。

でスタックトレース(とエラー値)を保持することができます。 sys.exc_info() しかし これは、よりエラーが発生しやすい そして は、Python 2 と 3 の間の互換性に問題があります。 を使用することを好みます。 raise をリレイズしてください。

説明すると、-は sys.exc_info() は、型、値、トレースバックを返します。

type, value, traceback = sys.exc_info()

これはPython 2の構文です - Python 3とは互換性がないことに注意してください。

raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

もし必要なら、新しいレイズで起こることを変更することができます - たとえば、新しい args をインスタンスに追加します。

def error():
    raise ValueError('oops!')

def catch_error_modify_message():
    try:
        error()
    except ValueError:
        error_type, error_instance, traceback = sys.exc_info()
        error_instance.args = (error_instance.args[0] + ' <modification>',)
        raise error_type, error_instance, traceback

そして、引数を変更しながら、全体のトレースバックを保存しています。なお、これは ベストプラクティスではありません であり、それは 無効な構文 は、Python 3では、互換性を維持することが非常に難しくなっています。

>>> catch_error_modify_message()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in catch_error_modify_message
  File "<stdin>", line 2, in error
ValueError: oops! <modification>

パイソン3 :

raise error.with_traceback(sys.exc_info()[2])

繰り返しになりますが、トレースバックを手動で操作するのは避けてください。それは 効率が悪い とエラーが発生しやすくなります。また、スレッディングを使用している場合や sys.exc_info 特に、制御フローに例外処理を使用している場合は、個人的には避けたいところです)。

Python 3, 例外連鎖

Python 3 では、トレースバックを保持するために例外を連結することができます。

raise RuntimeError('specific message') from error

意識してください。

  • これ が行います。 発生したエラーの種類を変更できるようにし
  • これは ない Python 2 と互換性があります。

非推奨のメソッド

これらは簡単に隠れることができ、プロダクションコードに入り込むことさえあります。例外を発生させたいのに、それらをやると例外が発生します。 が、意図したものではありません!

Python 2 では有効だが、Python 3 では無効である。 は以下の通りです。

raise ValueError, 'message' # Don't do this, it's deprecated!

のみ Pythonのかなり古いバージョンで有効 (2.4以下)の場合、まだ文字列を上げる人を見かけるかもしれません。

raise 'message' # really really wrong. don't do this.

すべての最新バージョンでは、これは実際に TypeError を発生させているわけではありませんから。 BaseException という型があります。正しい例外をチェックせず、問題を認識しているレビュアーがいなければ、生産に支障をきたす可能性があります。

使用例

自分のAPIの利用者が間違った使い方をした場合に、Exceptionを発生させて警告を出す。

def api_func(foo):
    '''foo should be either 'baz' or 'bar'. returns something very useful.'''
    if foo not in _ALLOWED_ARGS:
        raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

必要に応じて、独自のエラータイプを作成する

<ブロッククオート

except"に入るように、わざとエラーを出したいのですが。

アプリケーションに何か問題があることを示したい場合は、例外階層の適切な箇所をサブクラス化するだけで、独自のエラータイプを作成することができます。

class MyAppLookupError(LookupError):
    '''raise this when there's a lookup error for my app'''

と使用方法を説明します。

if important_key not in resource_dict and not ok_to_be_missing:
    raise MyAppLookupError('resource is missing, and that is not ok.')