web-dev-qa-db-ja.com

何かを「試して」、例外をキャッチするか、最初に例外を回避できるかどうかをテストする方が良いですか?

ifが有効か、またはtryをテストして例外をキャッチする必要がありますか?

  • 1つの方法が好ましいと言っている確かなドキュメントはありますか?
  • もう1つPythonicですか?

たとえば、私は:

if len(my_list) >= 4:
    x = my_list[3]
else:
    x = 'NO_ABC'

または:

try:
    x = my_list[3]
except IndexError:
    x = 'NO_ABC'

いくつかの考え...
PEP 2 言います:

エラーが黙って渡されることはありません。
明示的に沈黙しない限り。

tryの代わりにifを使用することは、エラーなしに渡されると解釈されるべきですか?もしそうなら、あなたはこのようにそれを使用することによって明示的にそれを黙らせているので、それをOKにしますか?


私はnot1つの方法しかできない状況を参照しています。例えば:

try:
    import foo
except ImportError:
    import baz
123
chown

その結果、_try/except_よりも_if/else_を優先する必要があります。

  • スピードアップ(余分なルックアップを防止するなど)
  • よりクリーンなコード(より少ない行/読みやすい)

多くの場合、これらは密接に関連しています。


スピードアップ

次の方法で長いリスト内の要素を見つけようとする場合:

_try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'
_

tryは、indexがおそらくリストにあり、IndexErrorが通常発生しない場合に最適なオプションです。これにより、if index < len(mylist)による追加のルックアップの必要性を回避できます。

Pythonは、あなたが扱う例外の使用を推奨しています Dive Into Python からのフレーズです。あなたの例は、例外を黙って渡すのではなく、例外を(優雅に)処理するだけでなく、例外はexceptionalインデックスが見つからない場合(したがって、Wordexception!)。


クリーナーコード

公式のPythonドキュメンテーションの言及 [〜#〜] eafp [〜#〜]許可よりも許しを求める方が簡単ですおよび Rob Knight は、 エラーを回避するのではなくキャッチする、クリーンで読みやすいコードになります。彼の例は次のように言っています:

さらに悪い(LBYL「あなたは飛躍する前に見てください」)

_#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit:
    return None
Elif len(s) > 10:    #too many digits for int conversion
    return None
else:
    return int(str)
_

より良い(EAFP:許可よりも許しを求める方が簡単です)

_try:
    return int(str)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None
_
146
Remi

この特定のケースでは、まったく別のものを使用する必要があります。

x = myDict.get("ABC", "NO_ABC")

ただし、一般的には:テストが頻繁に失敗すると予想される場合は、ifを使用します。テストが単に操作を試行し、失敗した場合に例外をキャッチすることに比べてコストが高い場合は、tryを使用します。これらの条件のいずれも当てはまらない場合は、読みやすいものを選択してください。

18
duskwuff

tryガード内ではなく、直接exceptifを使用する場合は、競合状態の可能性がある場合はalwaysを実行する必要があります。たとえば、ディレクトリが存在することを確認する場合は、これを実行しないでください。

import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

別のスレッドまたはプロセスがisdirmkdirの間にディレクトリを作成する場合、終了します。代わりに、これを行います:

import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

'foo'ディレクトリを作成できない場合にのみ終了します。

9
John Cowan

失敗する前に何かが失敗するかどうかを確認するのが簡単な場合は、おそらくそれを支持すべきです。結局、例外(それらに関連するトレースバックを含む)の構築には時間がかかります。

例外は次の場合に使用する必要があります。

  1. 予期しないもの、または...
  2. 複数のレベルのロジックをジャンプする必要がある場合(例:breakで十分でない場合)、または...
  3. 何が事前に例外を処理するかを正確に知らないもの、または...
  4. 失敗を事前に確認するのに費用がかかるもの(単に操作を試みることに比べて)

多くの場合、実際の答えは「どちらでもない」ことに注意してください。たとえば、最初の例では、実際にshouldを行うのは.get()を使用してデフォルトを提供するだけです。

x = myDict.get('ABC', 'NO_ABC')
7
Amber

他の投稿が言及するように、それは状況に依存します。特に大規模なプロジェクトでデータを使用する場合、事前にデータの有効性を確認する代わりにtry/exceptを使用すると、いくつかの危険があります。

  • Tryブロックのコードは、例外がキャッチされる前にあらゆる種類の大混乱を引き起こす可能性があります-ifステートメントを使用して事前に確認する場合は、これを回避できます。
  • Tryブロックで呼び出されたコードがTypeErrorやValueErrorなどの一般的な例外タイプを発生させた場合、実際にキャッチしようとしていた同じ例外をキャッチできない可能性があります。例外が発生する可能性のある行。

たとえば、あなたが持っていたと仮定します:

try:
    x = my_list[index_list[3]]
except IndexError:
    x = 'NO_ABC'

IndexErrorは、index_listまたはmy_listの要素を取得しようとしたときに発生したかどうかについては何も言いません。

4
Peter

Ifの代わりにtryを使用することは、エラーをサイレントに渡すと解釈されるべきですか?もしそうなら、あなたはこのようにそれを使用することによって明示的にそれを黙らせているので、それをOKにしますか?

tryを使用することは、エラーが通過する可能性があることを認めることです。 exceptを使用すると、まったくパスしません。

try: except:ロジックがより複雑な場合は、if: else:を使用することをお勧めします。単純なものは複雑なものよりも優れています。複雑は複雑よりも優れています。そして、許可よりも許しを求める方が簡単です。

「エラーが黙って渡されるべきではない」とは、コードがあなたが知っている例外を発生させる可能性があり、設計が可能性を認めているが、例外に対処する方法で設計していない場合です。私の見解では、明示的にエラーを黙らせることは、passブロック内でexceptのようなことをすることです。これは、「何もしない」ことが本当に正しいエラー処理であるという理解の下でのみ行われるべきです特定の状況。 (これは、よく書かれたコードのコメントがおそらく本当に必要だと思う数少ない時間の1つです。)

ただし、特定の例では、どちらも適切ではありません。

x = myDict.get('ABC', 'NO_ABC')

誰もがこれを指摘している理由-一般的に理解したいという願望と、より良い例を思い付かないことを認めたとしても-同等のサイドステップが実際に非常に多くの場合に存在し、それらを探すことが問題解決の最初のステップ。

4
Karl Knechtel

制御フローに_try/except_を使用するときは常に、次のことを自問してください。

  1. tryブロックが成功したときと失敗したときを確認するのは簡単ですか?
  2. tryブロック内のall副作用を知っていますか?
  3. tryブロックが例外をスローするallケースを知っていますか?
  4. tryブロックの実装が変更されても、制御フローは期待どおりに動作しますか?

これらの質問の1つ以上に対する答えが「いいえ」である場合、多くの許しが求められる可能性があります。おそらくあなたの将来の自己から。


例。私は最近、次のような大きなプロジェクトでコードを見ました:

_try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)
_

プログラマーと話すと、意図した制御フローは次のようになりました。

Xが整数の場合、y = foo(x)を実行します。

Xが整数のリストの場合、y = bar(x)を実行します。

これは、fooがデータベースクエリを作成し、xが整数の場合にクエリが成功し、ProgrammingErrorがリストの場合にxをスローするため、機能しました。

ここで_try/except_を使用するのは悪い選択です。

  1. 例外の名前ProgrammingErrorは、実際の問題(xは整数ではない)を与えないため、何が起こっているのかがわかりにくくなります。
  2. ProgrammingErrorは、データベース呼び出し中に発生し、時間を無駄にします。 fooが例外をスローする前、または他のシステムの状態を変更する前にデータベースに何かを書き込むことが判明した場合、事態は本当に恐ろしくなります。
  3. ProgrammingErrorが整数のリストである場合にのみxが発生するかどうかは不明です。たとえば、fooのデータベースクエリにタイプミスがあるとします。これにより、ProgrammingErrorも発生する場合があります。その結果、xが整数のときにbar(x)も呼び出されるようになりました。これにより、不可解な例外が発生したり、予期しない結果が生じる可能性があります。
  4. _try/except_ブロックは、fooの将来のすべての実装に要件を追加します。 fooを変更するときはいつでも、リストの処理方法を考え、ProgrammingErrorをスローするか、AttributeErrorをスローするか、エラーをまったく発生させないようにする必要があります。
2
Elias Strehle

一般的な意味については、 Pythonのイディオムとアンチイディオム:例外 を読むことを検討してください。

特定のケースでは、他の人が述べたように、 dict.get() を使用する必要があります。

get(key [、default])

Keyが辞書にある場合はkeyの値を返し、そうでない場合はデフォルトを返します。デフォルトが指定されていない場合は、デフォルトでNoneに設定されるため、このメソッドはKeyErrorを発生させません。

0
etuardu