web-dev-qa-db-ja.com

この場合に「広すぎる例外」を防ぐ方法は?

失敗する可能性のある関数のリストを取得しました。失敗した場合、スクリプトを停止せずに、次の関数を続行します。

私はこのようなものでそれを実行しています:

list_of_functions = [f_a,f_b,f_c]
for current_function in list_of_functions:
    try:
        current_function()
    except Exception:
        print(traceback.format_exc())

正常に動作していますが、PEP8に準拠していません。

例外をキャッチする場合は、裸のexcept:句を使用する代わりに、可能な限り特定の例外に言及してください。

たとえば、次を使用します。

try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

裸のexcept:句はSystemExitおよびKeyboardInterrupt例外をキャッチし、Control-Cでプログラムを中断することを難しくし、他の問題を隠すことができます。プログラムエラーを通知するすべての例外をキャッチする場合は、Exception:以外を使用します(bare exceptはBaseException:以外と同等です)。

経験則として、裸の「except」句の使用を2つのケースに制限することをお勧めします。

例外ハンドラーがトレースバックを出力または記録する場合;少なくともユーザーはエラーが発生したことを認識します。

コードでクリーンアップ作業を行う必要があるが、raiseを使用して例外を上方に伝播させる場合。 try ... finallyは、このケースを処理するためのより良い方法です。

これをどのように良い方法ですか?

23
Blusky

あなたが引用したPEP8ガイドは、あなたがエラーをログに記録しているならば、あなたのケースで裸の例外を使うことは大丈夫であることを示唆しています。できる限り多くの例外をカバー/対処する方法を知ってから、残りとpassを記録する必要があると思います。

import logging

list_of_functions = [f_a,f_b,f_c]
for current_function in list_of_functions:
    try:
        current_function()
    except KnownException:
        raise
    except Exception as e:
        logging.exception(e)
25
Ed Smith

Yourtrack.jetbrains.comの issue PY-9715 から:

"Too broad exception clauses" inspection

pep-0348 から:

BaseException

すべての例外が継承する必要があるスーパークラス。その名前は、それ自体が例外であると同時に例外階層のベースにあることを反映するために選択されました。 「Raisable」は名前と見なされましたが、その名前が例外そのものであるという事実を適切に反映していないため、引き継がれました。

BaseExceptionの直接継承は想定されておらず、一般的なケースでは推奨されません。ほとんどのユーザー定義の例外は、代わりにExceptionから継承する必要があります。これにより、キャッチする必要のあるすべての例外をキャッチするという一般的なケースで、例外のキャッチが引き続き機能します。 BaseExceptionの直接継承は、まったく新しいカテゴリの例外が必要な場合にのみ行う必要があります。

ただし、BaseException以外のすべての例外を盲目的にキャッチする必要がある場合は機能します。

1
xgqfrms

まれに、一般的な例外をキャッチすることが正当化され、PEP8検査をだます方法があると思います。

list_of_functions = [f_a,f_b,f_c]
for current_function in list_of_functions:
try:
    current_function()
except (ValueError, Exception):
    print(traceback.format_exc())

ValueErrorを他のものに置き換えることができます。それは(少なくともPyCharmで)私には有効です。

0

おそらく、各関数で異なる例外が発生する可能性があるということですか? except節で例外タイプに名前を付ける場合、クラス名だけでなく、例外を参照する任意の名前を指定できます。

例えば。

def raise_value_error():
    raise ValueError

def raise_type_error():
    raise TypeError

def raise_index_error():
    doesnt_exist

func_and_exceptions = [(raise_value_error, ValueError), (raise_type_error, TypeError), 
    (raise_index_error, IndexError)]

for function, possible_exception in func_and_exceptions:
   try:
       function()
   except possible_exception as e:
       print("caught", repr(e), "when calling", function.__name__)

プリント:

caught ValueError() when calling raise_value_error
caught TypeError() when calling raise_type_error
Traceback (most recent call last):
  File "run.py", line 14, in <module>
    function()
  File "run.py", line 8, in raise_index_error
    doesnt_exist
NameError: name 'doesnt_exist' is not defined

もちろん、各例外が発生したときに何をすべきかわからないままになります。しかし、それを無視して続行したいだけなので、それは問題ではありません。

0
Dunes