Pythonで例外を手動で発生(スロー)する


2266

後でexceptブロックを介してキャッチできるように、Pythonで例外を発生させるにはどうすればよいですか?

回答:


2938

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

これらの引数はargsExceptionオブジェクトの属性によってアクセスされます。例えば:

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

プリント

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

Python 2.5では、ユーザーにExceptionsのサブクラスを作成してを使用しないように促すmessageためBaseExceptionに実際の属性が追加されましたargsmessageargsの導入と元の非推奨は撤回されました

ベストプラクティス: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、インスタンスにnew を設定します。

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>

ではPythonの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.

すべての最新バージョンではTypeErrorBaseException型を発生させないため、これは実際にはを発生させます。適切な例外をチェックしておらず、問題を認識しているレビュー担当者がいない場合、本番環境に入る可能性があります。

使用例

例外を発生させて、APIを間違って使用している場合に、APIの消費者に警告します。

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

適切なときに独自のエラータイプを作成する

「わざとエラーを出して、それが例外になるようにしたい」

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

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.')

19
これをありがとう、それがまさに私が必要としていたことです。裸のはraise、私は、スタックトレースを壊すことなく、コード実行の複数のレベルでカスタムエラーのデバッグを実行できるようにするために必要なものです。
CaffeineConnoisseur 2017

これは素晴らしい答えです。しかし、私はまだ多くの2.7コードを使用しており、入力ファイルの位置や一部の変数の値などの予期しない例外に情報を追加したいが、元のスタックと例外は保持することがよくあります。ログを記録することはできますが、たとえば親コードが最終的に処理する場合など、ログに記録したくない場合があります。raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]私がしたいことをしているようで、私はそれで問題に遭遇したことがありません。しかし、それはハックに感じられ、受け入れられた慣習ではありません。もっと良い方法はありますか?
Michael Scheper

2
@brennanyoungそのコンテキストでは、SyntaxErrorを発生させるのは混乱する可能性があると思います-おそらくカスタム例外を発生させるべきです。ここで方法を説明します:stackoverflow.com/a/26938914/541136
アーロンホール

2
完全な引用は「システムに終了しない組み込みの例外はすべてこのクラスから派生しています。ユーザー定義の例外もすべてこのクラスから派生する必要があります。」-これは主にException、親クラスとして派生しない4つの例外の1つを使用してはならないことを意味します。より具体的なサブクラスを作成できます。
アーロンホール

1
ベストプラクティス:except節」の例では、未定義のAppError例外を使用しています。組み込みのエラーのように使用する方が良いかもしれAttributeError
Stevoisiak

530

これをしないでください。裸を上げるとException、絶対にありません行うには正しいこと。代わりにアーロンホールの優れた答えをご覧ください。

これより多くのpythonicを取得することはできません:

raise Exception("I know python!")

詳細について、Pythonのレイズステートメントのドキュメントをご覧ください。


67
いやいや!これにより、キャッチした内容を特定できる可能性がなくなります。それを行うのは完全に間違った方法です。これではなく、Aaron Hallの優れた答えを見てください。このような場合は、1つの回答に複数の反対票を投じることができればいいのにと思います。
Dawood ibnカリーム2015年

27
@PeterR反対票が少ないのも同様に恐ろしいことです。誰でもこの答えを読んで、これまでにこれをしないでください!正解はアーロンホールのものです。
Dawood ibnカリーム2015

6
これがなぜ間違っているか、またはそれほど悪いのかについて、より詳細な説明があるはずだと思います。
チャーリーパーカー

9
@CharlieParkerあります。アーロンホールの回答の最初の部分です。
Dinei 2017

5
この回答に削除フラグを設定できないのはなぜですか?すでに93の反対投票があります!
codeforester 2017年

55

Python3には、例外を発生させるための4つの異なる構文があります。

1. raise exception 
2. raise exception (args) 
3. raise
4. raise exception (args) from original_exception

1.例外を発生させるか2.例外を発生させる(引数)

を使用raise exception (args) して例外を発生 argsさせた場合、以下の例に示すように、例外オブジェクトを印刷するときにが印刷されます。

  #raise exception (args)
    try:
        raise ValueError("I have raised an Exception")
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error I have raised an Exception 



  #raise execption 
    try:
        raise ValueError
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error 

3.上げる

raise引数のないステートメントは、最後の例外を再評価します。これは、例外をキャッチした後に何らかのアクションを実行する必要があり、それを再度発生させたい場合に役立ちます。しかし、以前に例外がなかった場合、raiseステートメントはTypeError例外を発生させ ます。

def somefunction():
    print("some cleaning")

a=10
b=0 
result=None

try:
    result=a/b
    print(result)

except Exception:            #Output ->
    somefunction()           #some cleaning
    raise                    #Traceback (most recent call last):
                             #File "python", line 8, in <module>
                             #ZeroDivisionError: division by zero

4. original_exceptionから例外(引数)を発生させる

このステートメントは、次の例に示すように、別の例外に応答して発生する例外に元の例外の詳細を含めることができる例外チェーンを作成するために使用されます。

class MyCustomException(Exception):
pass

a=10
b=0 
reuslt=None
try:
    try:
        result=a/b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
        print("MyException",exp)
        print(exp.__cause__)

出力:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero

7
PEP8が優先exception(args)することに注意してくださいexception (args)
Gloweye

またraise exception(args) from None、現在アクティブな例外が処理され、関心がなくなったとも言えます。あなたが内部で例外を発生させる場合にはそうでない場合はexceptブロックし、それが処理されない、両方の例外のトレースバックがメッセージで区切ら表示されます「上記の例外の取り扱い時には、別の例外が発生しました」
cg909

35

予期しない状況に対応して例外をスローする必要があり、キャッチするつもりはないが、単に失敗して、発生した場合にそこからデバッグできるようにするという一般的なケースでは、最も論理的なケースはAssertionError

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)

19
これは、アサーションに問題がないため(ここでは何も行われていないため)ValueErrorよりも良いケースAssertionErrorです。問題は値にあります。この場合に本当に必要なAssertionError場合は、と書いてくださいassert distance > 0, 'Distance must be positive'。ただし、アサーションをオフにできるので(python -O)、そのようにエラーチェックしないでください。
2ビットのアルケミスト

1
@ Two-BitAlchemist良い点。上記の簡単な例を書いたとき、アイデアは単純化で失われました。多くの類似したケースでは、特定の値に関連付けられていない状態です。むしろ、その意味は「制御フローがここに到達してはならない」です。
Evgeni Sergeev

2
@ Two-BitAlchemistアサーションはオフにすることができますが、エラーチェックには使用しないでください。
Evgeni Sergeev

まあそれは依存します。私が配布するつもりだったプログラムで、これが唯一のエラーチェックになることは許しません。一方、同僚専用のプログラムを作成して、で実行する場合は自己責任で使用するように伝えることができます-O
2ビットの錬金術師

1
@ Two-BitAlchemist私にとって、アサーションの役割自体はエラーチェックではありません(テストの目的です)が、特定のバグが通過できないフェンスをコード内に設定します。そのため、必然的に発生するバグの追跡と分離が容易になります。これはほんの少しの労力でよい習慣ですが、テストには多くの労力と時間がかかります。
Evgeni Sergeev

12

まず既存の回答を読んでください。これは単なる補遺です。

引数の有無にかかわらず例外を発生させることができることに注意してください。

例:

raise SystemExit

プログラムを終了しますが、何が起こったのかを知りたい場合があるので、これを使用できます。

raise SystemExit("program exited")

これにより、プログラムを閉じる前に「プログラム終了」がstderrに出力されます。


2
これは、OOPパラダイムに反するものではありませんか?最初のケースはクラス参照をスローし、2番目のケースはSystemExitのインスタンスをスローすると思います。raise SystemExit()より良い選択ではないでしょうか?なぜ最初のものはうまくいくのですか?
BURNY

2

例外をスローする別の方法はassertです。アサートを使用して、条件が満たされていることを確認できますAssertionError。満たされていない場合は、条件が発生します。詳細はこちらをご覧ください

def avg(marks):
    assert len(marks) != 0,"List is empty."
    return sum(marks)/len(marks)

mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))

mark1 = []
print("Average of mark1:",avg(mark1))

2

注:一般的な例外を処理する必要がある場合があります。一連のファイルを処理してエラーをログに記録している場合、ファイルで発生したエラーをキャッチしてログに記録し、残りのファイルの処理を続行することができます。その場合、

try:
    foo() 
except Exception as e:
    print(str(e)) # Print out handled error

それを行う良い方法をブロックします。あなたはまだしたいと思うraiseあなたは、彼らが何を意味するか知っているのでかかわらず、特定の例外。


0

あなたはそのためにPythonのレイズステートメントを学ぶべきです。これは、tryブロック内に保持する必要があります。例-

try:
    raise TypeError            #remove TypeError by any other error if you want
except TypeError:
    print('TypeError raised')
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.