python:発生した例外の種類を知るにはどうすればよいですか?


230

メインプログラムから呼び出される関数があります。

try:
    someFunction()
except:
    print "exception happened!"

ただし、関数の実行の途中で例外が発生するため、そのexcept部分にジャンプします。

someFunction()例外が発生する原因となったで何が起こったのかを正確に確認するにはどうすればよいですか?


9
今まで、これまで裸絶対に使用しないでくださいexcept:(裸なしraise)を除いて、多分好ましくないし、プログラムごとに一度、と。
マイクグラハム

複数のexcept句を使用する場合、例外タイプを確認する必要はありません。これは、特定の例外タイプに応じて機能するために通常行われることです。
Rik Poggi

3
例外のタイプを気にする場合は、論理的に発生する可能性のある例外のタイプをすでに検討しているためです。
Karl Knechtel、2012年

3
exceptブロック内では、sys.exc_info()関数を介して例外を利用できます– この関数は、現在処理されている例外に関する情報を提供する3つの値のタプルを返します。
Piotr Dobrogost 2017

回答:


384

他の回答はすべて、一般的な例外をキャッチするべきではないことを指摘していますが、誰もその理由を伝えたくないようです。これは、「ルール」を破ることができるときを理解するために不可欠です。ここに説明があります。基本的に、それはあなたが隠さないためです:

したがって、これらのことを何もしないように気を付けている限り、一般的な例外をキャッチしても問題ありません。たとえば、次のような別の方法でユーザーに例外に関する情報を提供できます。

  • 例外をGUIのダイアログとして表示する
  • 例外をワーカースレッドまたはプロセスからマルチスレッドまたはマルチプロセッシングアプリケーションの制御スレッドまたはプロセスに転送する

では、一般的な例外をキャッチする方法は?いくつかの方法があります。例外オブジェクトだけが必要な場合は、次のようにします。

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

見逃せない方法でユーザーの注意を引くこと確認してくださいmessage!上記のように、メッセージが他の多くのメッセージに埋もれている場合は、印刷しても不十分な場合があります。ユーザーの注意を引くことができないのは、すべての例外を飲み込むことと同じであり、このページの回答を読んだ後に思い浮かんだはずの印象が1つある場合、これは良いことではありませんraiseステートメントでexceptブロックを終了すると、キャッチされた例外を透過的に再発生させることで問題が解決されます。

上記とexcept:引数なしでの使用の違いは2つあります。

  • ベアexcept:はあなたに検査する例外オブジェクトを与えません
  • 例外SystemExitKeyboardInterruptおよびGeneratorExit上記のコードではキャッチされません。これは一般的に必要なことです。例外階層を参照してください。

例外をキャッチしない場合と同じスタックトレースが必要な場合は、次のようにして取得できます(ただし、except句内にあります)。

import traceback
print traceback.format_exc()

loggingモジュールを使用する場合、次のように例外を(メッセージとともに)ログに出力できます。

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

より深く掘り下げてスタックを調べたい場合、変数などを調べたい場合は、exceptブロック内のモジュールのpost_mortem関数を使用します。pdb

import pdb
pdb.post_mortem()

この最後の方法は、バグを探すときに非常に役立つことがわかりました。


1
traceback.print_exc()は、より複雑な "" .join-thingと同じことを行うと思います。
Gurgeh、2012年

1
@Gurgehはい、しかし彼がそれを印刷するか、ファイルに保存するか、それをログに記録するか、それを使って他の何かをしたいのかはわかりません。
Lauritz V. Thaulow 2012年

私は反対票を投じなかったが、それはあなたが最初に巨大な脂肪の摩耗を入れて、これは必要ないはずだと言っていたからだと言いますが、これがどのように行われるかです。そして多分あなたは一般的な例外をキャッチすることを提案しているからです。
Rik Poggi、2012年

10
@Rik私はあなたがこのすべてをとても必要としていると思う。たとえば、GUIとバックエンドを備えたプログラムがあり、プログラムをスタックトレースで終了させる代わりに、バックエンドからのすべての例外をGUIメッセージとして表示したい場合などです。このような場合、一般例外をキャッチし、ダイアログのトレースバックテキストを作成し、例外もログに記録し、デバッグモードの場合は事後分析を入力する必要があります。
Lauritz V. Thaulow

18
@RikPoggi:素朴な考え方。他の誰かのコードからの例外をキャッチする必要があり、どの例外が発生するかわからない場合には、多くの合理的な状況があります。
stackoverflowuser2010 2016年

63

例外オブジェクトが属するクラスの名前を取得します。

e.__class__.__name__

また、print_exc()関数を使用すると、エラーメッセージの重要な情報であるスタックトレースも出力されます。

このような:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

次のような出力が得られます。

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

そして、出力と分析の後、コードは例外を処理しないことを決定し、単に実行することができraiseます:

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

出力:

special case of CustomException not interfering

そして、インタプリタは例外を出力します:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

raise元の例外がコールスタックのさらに上に伝搬し続けた後。(落とし穴の可能性に注意)新しい例外を発生させると、新しい(より短い)スタックトレースが保持されます。

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

出力:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

トレースバックに、元の例外の原因であるcalculate()行からの関数が含まれていないことに注意してください。9e


あなたは文字列としてトレースバックを保存したい場合は、使用することができるtraceback.format_exc()だけでなく
Stevoisiak

1
e.__class__.__name__type(e).__name__上記の回答で示唆されているように、これはと同じですか?
information_interchange

1
@information_interchangeはい。質問と受け入れられた回答の内容は、時間とともに完全に変化しました。他の参加者がSO機械から通知されないのは残念です:(
Alex

14

try: ... exceptこれは非常に広範であるため、通常はすべての可能な例外をキャッチするべきではありません。何らかの理由で発生すると予想されるものをキャッチしてください。本当に必要な場合、たとえばデバッグ中に問題についてさらに詳しく知りたい場合は、

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.

17
ここでは「決して」という言葉の使用はそれほど間違っていません。私はtry: ... except Exception:、ネットワークに依存するライブラリの使用や、変なものを彼女に送信する可能性のあるデータマッサージ師など、多くのものを使用しています。当然、私も適切なロギングを行っています。これは、入力データに単一のミスがあった場合でもプログラムが動作を継続できるようにするために重要です。
2013

3
を使用してメールを送信するときに発生する可能性のあるすべての例外をキャッチしようとしたことがありますsmtplibか?
linusg

1
すべての例外をキャッチする必要があるいくつかの特別な場合がありますが、一般的なレベルでは、予期しないエラーを誤って隠さないように、期待どおりのものをキャッチする必要があります。もちろん、適切なロギングも良い考えです。
hochl 2016年

1
すべての例外をキャッチすることは完全に合理的です。サードパーティのライブラリを呼び出している場合、そのライブラリで発生する例外がわからない場合があります。このような場合、唯一の解決策は、たとえばファイルに例外を記録するなど、すべての例外をキャッチすることです。
stackoverflowuser2010 2016年

わかりました。正解です。キャッチオールの有効な使用例があることを明確にするために、答えを言い換えます。
2016年

10

somefunction非常に悪いコード化されたレガシー関数でない限り、あなたが求めていることは必要ありません。

複数のexcept句を使用して、さまざまな方法でさまざまな例外を処理します。

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

重要な点は、一般的な例外をキャッチするのではなく、必要なものだけをキャッチすることです。予期しないエラーやバグを隠したくないと私は確信しています。


8
サードパーティのライブラリを使用している場合、その中で発生する例外がわからない場合があります。どうすればそれらすべてを個別にキャッチできるでしょうか?
stackoverflowuser2010 2016年

8

ほとんどの回答はexcept (…) as (…):構文を指し示していますが(正しくそうです)、同時に、象がsys.exc_info()機能している部屋で象について話したいと思う人はいません。以下からのドキュメントSYSモジュール(強調鉱山):

この関数は、現在処理されている例外に関する情報を提供する3つの値のタプルを返します。
(…)
スタックのどこでも例外が処理されていない場合、3つのNone値を含むタプルが返されます。それ以外の場合、返される値は(タイプ、値、トレースバック)です。その意味は次のとおりです。typeは、処理される例外のタイプ(BaseExceptionのサブクラス)を取得します。valueは例外インスタンス(例外タイプのインスタンス)を取得します。tracebackは、例外が最初に発生した時点でコールスタックをカプセル化するtracebackオブジェクト(リファレンスマニュアルを参照)を取得します。

発生した例外の種類を知るにsys.exc_info()どうすればよいですか?の元の質問に対する最も直接的な回答として扱うことができると思います


1
これは、どの例外が発生しているのかという問題を解決するので、正解exceptです。ベアの代わりに何を配置すればよいですか。完全を期すために、exctype, value = sys.exc_info()[:2]で使用できる例外のタイプを示しexceptます。
Ondrej Burkert、2018年

5

試行:例外を除くsomeFunction()、exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))

使用時の-1 exc.__class__.__name__は、アレックスの回答で既に提案されていました– stackoverflow.com/a/9824060/95735
Piotr Dobrogost 18/07/23

3

これらの回答はデバッグには適していますが、プログラムで例外をisinstance(e, SomeException)テストする場合、サブクラスSomeExceptionもテストするので便利です。そのため、例外の階層に適用される機能を作成できます。


1

例外を処理する方法は次のとおりです。簡単な場合は問題を解決してみて、可能であればより望ましい解決策を後で追加するという考え方です。例外を生成するコードの問題を解決しないでください。そうしないと、そのコードは、本来のアルゴリズムである本来のアルゴリズムを追跡できなくなります。ただし、問題を解決するために必要なデータを渡し、それを生成するコードの外で問題を解決できない場合に備えてラムダを返します。

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

今のところ、アプリの目的に正直に考えたくないので、複雑なソリューションを追加していません。しかし、将来的には、可能な解決策について詳しく知ると(アプリはより設計されているため)、でインデックス付けされduringた解決策の辞書を追加することができます。

示されている例では、「app.p」ファイルが誤って削除された場合など、別の場所に保存されているアプリデータを探すことが1つの解決策になる場合があります。

現時点では、例外ハンドラーを書くことは賢いアイデアではないため(アプリの設計が進化するため、それを解決する最良の方法はまだわかりません)、実行しているように機能する簡単な修正を返すだけです。初めてのアプリ(この場合)。


0

Lauritzの回答に追加するために、例外処理用のデコレーター/ラッパーを作成し、ラッパーはどのタイプの例外が発生したかをログに記録しました。

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

これは、デコレータを使用してクラスメソッドまたはスタンドアロン関数で呼び出すことができます。

@general_function_handler

完全な例については、私のブログを参照してください:http : //ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/


0

Lauritzの推奨どおりに、次のように開始できます。

except Exception as ex:

そしてそれをprint ex好きに:

try:
    #your try code here
except Exception as ex:
    print ex

あなたの答えが単独で立つように少し詳しく説明できますか?
GHC

1
確認:試し:あなたはこのようなキャッチした例外を印刷することができます#yourトライコードをここに元のように例外を除い:プリントEXエラーが印刷されます今
グラ


-2

あなたの質問は、「例外が発生する原因となったsomeFunction()で何が起こったかを正確に確認するにはどうすればよいですか?」

本番用コードで予期しない例外を処理する方法(多くの回答が想定されている)について質問しているのではなく、開発中に特定の例外を引き起こしている原因を見つける方法について質問しているようです。

最も簡単な方法は、キャッチされない例外が発生した場所で停止できるデバッガーを使用することです。できれば終了しないので、変数を検査できます。たとえば、EclipseオープンソースIDEのPyDevはそれを行うことができます。Eclipseでこれを有効にするには、デバッグパースペクティブを開きManage Python Exception BreakpointsRunメニューで選択して、を確認しSuspend on uncaught exceptionsます。


-4

例外をキャッチしないでください。Pythonが出力するトレースバックでは、発生した例外がわかります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.