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


139

if何かが有効かどうかをテストする必要tryがありますか、それとも例外をキャッチするだけですか?

  • 一方通行の方が望ましいという確固とした文書はありますか
  • 一方向にもっと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 20は言う:

エラーは黙って渡ってはなりません。
明示的に沈黙させない限り。

try代わりにを使用すると、if警告なしにエラーが発生したと解釈されますか?もしそうなら、それをこのように使用して明示的に沈黙させ、それでそれを大丈夫にしますか?


私は、物事が一方向にしかできない状況について言及していません。例えば:

try:
    import foo
except ImportError:
    import baz

回答:


161

あなたは選ぶべきtry/except超えるif/elseという結果であれば

  • スピードアップ(例えば、余分なルックアップを防ぐことによる)
  • よりクリーンなコード(少ない行/読みやすい)

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


スピードアップ

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

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

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

Pythonは例外の使用奨励あなたが扱うからフレーズです飛び込むのPythonを。あなたの例は、黙って通過させるのではなく、(優雅に)例外を処理するだけでなく、インデックスが見つからないという例外的な場合にのみ発生します(したがって、例外!)。


よりクリーンなコード

公式Pythonドキュメントは言及EAFPを許可よりも許しを求めることが容易ロブナイトのことをノートにそれらを避けるのではなく、エラーをキャッチすると、コードを読み取るために、きれいに簡単になります。彼の例は次のように言っています:

悪い(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(s)

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

try:
    return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None

if index in mylistインデックスが可能性のあるインデックスではなく、mylistの要素であるかどうかをテストします。if index < len(mylist)代わりにしたいでしょう。
ychaouche 2013年

1
これは理にかなっていますが、int()に対してスローされる可能性のある例外を明確にするドキュメントはどこにありますか。docs.python.org/3/library/functions.html#intここにこの情報が見つかりません。
BrutalSimplicity

逆に、あなたが(からこのケースを追加することができます。DOCオフあなたが好む必要があるとき、あなたの答えに)if/elseよりtry/catch
rappongy

20

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

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

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


1
+1されているコードサンプルの下の説明。
ktdrv 2011

4
これは彼が求めていたものではないことはかなり明らかだと思います。彼は投稿をさらに明確にするために編集しました。
agf

9

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

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」ディレクトリを作成できない場合にのみ終了します。


7

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

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

  1. 予期しないこと、または...
  2. 複数レベルのロジックをジャンプする必要があるもの(たとえば、aでbreak十分に足りない場合)、または...
  3. 事前に例外を処理する対象が正確にわからない場合、または...
  4. 事前に失敗を確認するのにコストがかかる(操作を試みるだけの場合と比較して)

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

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

ただし、残念ながらif 'ABC' in myDict: x = myDict['ABC']; else: x = 'NO_ABC'実際にはを使用するよりも高速ですget。これは最も重要な基準であるとは言いませんが、注意する必要があります。
agf

@agf:明確で簡潔なコードを記述する方がよい。何かを後で最適化する必要がある場合は、戻って書き直すのは簡単ですが、c2.com / cgi / wiki?PrematureOptimization
Amber

それを知っています。私のポイントは、パフォーマンス特性が異なるため、ケース固有の代替手段がある場合でも、その場所if / elseを使用try / exceptできることです。
agf

@ agf、get()メソッドが将来のバージョンで(少なくとも)明示的に検索するのと同じくらい高速になるように改善されるかどうか知っていますか?ちなみに、2回検索する場合(dの 'ABC'のように:d ['ABC'])、試してみてください:KeyError:...以外のd ['ABC']は最速ではありませんか?
レミ

2
@Remiの遅い部分は.get()、Pythonレベルでの属性ルックアップと関数呼び出しのオーバーヘッドです。ビルトインでのキーワードの使用は、基本的にはCに適しています。すぐに速くなるとは思いません。これまでのようにiftry、読みdict.get()メソッドが返すポインタいくつかのパフォーマンスの情報を持っています。try辞書のサイズと同様に、ヒットとミスの比率が重要です(キーがほぼ常に存在する場合は、より速くなる可能性があります)。
agf 2011

5

他の投稿が述べているように、それは状況に依存します。特に大規模なプロジェクトでデータを使用する場合は、データの有効性を事前に確認する代わりに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

ifの代わりにtryを使用すると、黙って渡すエラーとして解釈されますか?もしそうなら、それをこのように使用して明示的に沈黙させ、それでそれを大丈夫にしますか?

を使用するtryと、エラーが発生する可能性があることを確認できます。これは、エラーを通知せずに通過させることとは逆です。使用するexceptと全く通れなくなります。

使い方はtry: except:例に好まれるif: else:ロジックがより複雑になります。単純なものは複雑なものよりも優れています。複雑は複雑よりも優れています。許可よりも許しを求める方が簡単です。

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

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

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

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


3

try/except制御フローに使用するときはいつでも、自問してみてください。

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

これらの質問の1つ以上の答えが「いいえ」である場合、要求する多くの許しがあるかもしれません。おそらくあなたの将来の自己からです。


例。最近、次のような大規模なプロジェクトのコードを見ました。

try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)

プログラマーと話すと、意図された制御フローは次のとおりであることがわかりました。

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

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

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

try/exceptここでの使用は悪い選択です:

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

0

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

あなたの特定のケースでは、他の人が述べたように、あなたは使うべきですdict.get()

get(key [、default])

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


リンクはこの状況をカバーしていないと思います。その例は、予想される状況で例外処理を使用するかどうかではなく、実際のエラーを表すものの処理に関するものです
agf 2011

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