web-dev-qa-db-ja.com

try = if in python

変数に値があることをテストするときに、tryまたはifのどちらの構成体を使用するかを決定する理由はありますか?

たとえば、リストを返すか、値を返さない関数があります。結果を処理する前に確認したい。次のうちどれがより望ましいでしょうか、そしてその理由は何ですか?

result = function();
if (result):
    for r in result:
        #process items

または

result = function();
try:
    for r in result:
        #process items
except TypeError:
    pass;

関連する議論:

Pythonでのメンバーの存在の確認

114
artdanil

Python推奨 [〜#〜] eafp [〜#〜] スタイル( "許可よりも許しを求める方が簡単です") [〜#〜] lbyl [〜#〜] style( "飛ぶ前に見える")。私にとっては、効率と読みやすさの問題です。

あなたの例では(リストまたは空の文字列を返す代わりに、関数はリストまたはNoneを返すことになっています)、99%のresultが実際に反復可能なものであれば、try/exceptアプローチを使用します。例外が本当に例外的な場合は、高速になります。 resultが50%を超えるNoneである場合は、おそらくifを使用する方が適切です。

いくつかの測定でこれをサポートするには:

>>> import timeit
>>> timeit.timeit(setup="a=1;b=1", stmt="a/b") # no error checking
0.06379691968322732
>>> timeit.timeit(setup="a=1;b=1", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.0829463709378615
>>> timeit.timeit(setup="a=1;b=0", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.5070195056614466
>>> timeit.timeit(setup="a=1;b=1", stmt="if b!=0:\n a/b")
0.11940114974277094
>>> timeit.timeit(setup="a=1;b=0", stmt="if b!=0:\n a/b")
0.051202772912802175

したがって、ifステートメントalwaysにはコストがかかりますが、try/exceptブロックを設定するのはほぼ無料です。しかし、Exceptionが実際に発生すると、コストははるかに高くなります。

モラル:

  • フロー制御にtry/exceptを使用してもまったく問題ありません(そして「Pythonic」)、
  • しかし、Exceptionsが実際に例外的である場合に最も意味があります。

Python docs:

[〜#〜] eafp [〜#〜]

許可よりも許しを求める方が簡単です。この一般的なPythonコーディングスタイルは、有効なキーまたは属性の存在を前提とし、仮定が偽である場合に例外をキャッチします。このクリーンで高速なスタイルは、多くのtryexceptステートメント:この手法は、Cなどの他の多くの言語に共通の [〜#〜] lbyl [〜#〜] スタイルとは対照的です。

197
Tim Pietzcker

関数は混合型(つまり、リストまたは空の文字列)を返すべきではありません。値のリストまたは単に空のリストを返す必要があります。その後、何もテストする必要はありません。つまり、コードは次のように折りたたまれます。

for r in function():
    # process items
13
Brandon

私が提供するコードが一見して明らかではなく、コードサンプルの後に説明を読む必要がある場合は、私のソリューションを無視してください。

「値が返されない」とは、戻り値がなしであることを意味すると仮定できますか? 「はい」の場合、または「値なし」がブール値で偽の場合、コードは基本的に「値なし」を「反復しない」として扱うため、次のことができます。

_for r in function() or ():
    # process items
_

function()がTrueではない何かを返す場合、空のタプルを反復処理します。つまり、反復を実行しません。これは本質的にLBYLです。

10
tzot

2番目の例は壊れています。文字列とリストの両方を反復処理できるため、コードはTypeError例外をスローしません。空の文字列またはリストを反復処理することも有効です。ループの本体をゼロ回実行します。

5
Dave Kirby

一般的に、私が得た印象は、例外は例外的な状況のために予約されるべきだということです。 resultが空にならないことが予想される場合(ただし、たとえば、ディスクがクラッシュした場合など)、2番目のアプローチは理にかなっています。一方、空のresultが通常の条件下で完全に妥当である場合、ifステートメントを使用してそれをテストする方が理にかなっています。

(より一般的な)シナリオを念頭に置いた:

# keep access counts for different files
file_counts={}
...
# got a filename somehow
if filename not in file_counts:
    file_counts[filename]=0
file_counts[filename]+=1

同等の代わりに:

...
try:
    file_counts[filename]+=1
except KeyError:
    file_counts[filename]=1
4
Managu

次のうちどれがより望ましいでしょうか、そしてその理由は何ですか?

この場合、飛躍する前に見てください。例外アプローチを使用すると、ループ本体のどこでもTypeErrorが発生し、キャッチされて破棄されます。これは望んでいないことであり、デバッグが難しくなります。

(ただし、ブランドンコルフマンに同意します。空のリストの代わりに「アイテムなし」に対してNoneを返すのは壊れています。これは、Pythonでは見られないはずのコーダーであるJava )

4
bobince

bobince は、2番目のケースをラップすると、ループ内のTypeErrorsもキャッチできることを指摘していますが、これは望んでいないことです。ただし、実際に試してみたい場合は、ループの前に反復可能かどうかをテストできます

result = function();
try:
    it = iter(result)
except TypeError:
    pass
else:
    for r in it:
        #process items

ご覧のとおり、かなりいです。私はそれを提案しませんが、完全性のために言及されるべきです。

3
AFoglia

パフォーマンスに関する限り、通常例外を発生させないコードにtryブロックを使用する方が、ifステートメントを毎回使用するよりも高速です。そのため、決定は異常なケースの確率に依存します。

1
grayger