web-dev-qa-db-ja.com

python:どのタイプの例外が発生したかをどうやって知るのですか?

メインプログラムによって呼び出される関数があります。

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

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

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

179
Shang Wang

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

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

  • 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

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

上記と、引数なしで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ブロック内のpdbモジュールの post_mortem 関数を使用します。

import pdb
pdb.post_mortem()

この最後の方法は、バグを見つけるときに非常に貴重であることがわかりました。

302

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

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    

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

37
Alex

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

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

somefunctionが非常に悪いコード化されたレガシー関数でない限り、あなたが求めているものは必要ないはずです。

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

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

主なポイントは、一般的な例外をキャッチするのではなく、必要な例外のみをキャッチすることです。予期しないエラーやバグを隠したくないと確信しています。

8
Rik Poggi

ほとんどの答えはexcept (…) as (…):構文を指しますが(正しいことです)、同時に、象がsys.exc_info()関数である部屋で象について話すことを望んでいません。 ドキュメント of sysモジュール(エンファシス鉱山)から:

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

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

7
Piotr Dobrogost

try:例外を除く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))
5
Mr. Me

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

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つの解決策です。

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

Lauritzが推奨するように開始できます。

except Exception as ex:

そして、次のようにprint exに:

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

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
rirwin