Python-'if foo in dict' vs 'try:dict [foo]'


24

これは、アヒルのタイピングの性質についての質問ではなく、pythonicを維持することについての質問です。

まず第一に-dictを扱うとき、特にdictの構造がかなり予測可能であり、特定のキーが通常存在しないが時々存在する場合、私は最初に2つのアプローチを考えます:

if myKey in dict:
    do_some_work(dict[myKey])
else:
    pass

そしてもちろん、イェ・オルデの「許し対許可」アプローチ。

try:
    do_some_work(dict[myKey])
except KeyError:
    pass

Pythonの旅人として、後者は多くの方が好まれているように感じますが、Pythonドキュメントtry/exceptsでは実際の間違いがある場合に好まれているように思えます。

時折の辞書にmyDictにキーがなく、常にそのキーを持っているとは限らないことがわかっている場合、try / exceptは文脈上誤解を招く可能性がありますか?これはプログラミングエラーではなく、単なるデータの事実です。この辞書には特定のキーがありませんでした。

try / except / else構文を見ると、これは特に重要であるように見えます。これは、tryがあまりにも多くのエラーをキャッチしないことを確認する際に非常に役立つようです。次のようなことができます:

try:
    foo += bar
except TypeError:
    pass
else:
    return some_more_work(foo)

それはおそらく悪いコードの結果であるあらゆる種類の奇妙なエラーを飲み込むことにつながるのではないでしょうか?上記のコードは2 + {}、あなたが追加しようとしていることを見るのを妨げているだけで、コードの一部がひどく間違っていることに気付かないかもしれません。私はすべてのタイプをチェックすることをお勧めしません。それがJavaScriptではなくPythonである理由です。継続することを可能にします。

上記の例は、ストローマンの議論のようなものであり、実際には意図的に悪いことを理解しています。しかし、better to ask forgiveness than permission私はPythonの信条を考えると仕方がありませんが、砂の中の線が実際にif / elseとtry / exceptの正しい適用の間のどこにあるのか、特に何が期待できるかを知っているとき作業しているデータ。

私はここで速度の懸念やベストプラクティスについても話していない、私はそれがいずれかの方法で行くことができるように見えるケースの知覚されたベン図に静かに混乱しているが、人々は試行/除外の側で誤ります「誰かがそれがPythonicだと言った」からです。この構文の適用について間違った結論を出しましたか?

python 

try-exceptの傾向はエラーを隠すリスクを伴うことに同意します。私の意見では、一般的に、あなたが見ると予想される条件に対してはtry-exceptを避けるべきであるということです(もちろん、この規則にはいくつかの例外が適用されます)。
ゴードンビーン

回答:


26

代わりにgetメソッドを使用します

some_dict.get(the_key, default_value)

...はがにないdefault_value場合に返される値です。を省略すると、キーが欠落している場合に返されます。the_keysome_dictdefault_valueNone

一般的に、Pythonでは、最初に何かをチェックするよりもtry / exceptを好む傾向があります- 用語集のEAFPエントリを参照してください。多くの「メンバーシップのテスト」関数は、舞台裏で例外を使用することに注意してください。


しかし、この種の同じ問題ではありませんか?を使用dictして作業を行い、見つかった内容に基づいて作業を行おうとしている場合if myDict.get(a_key) is not None: do_work(myDict[a_key])は、言葉遣いが多く、跳躍する前の暗黙的な検索のもう1つの例に加えて、読みにくくなっています。おそらく私はdict.get(a_key, a_default)正しいコンテキストで理解していないかもしれませんが、「スイッチ文ではないif / elses」や魔法の値を書く前兆のようです。私はこの方法が好きですが、より深く調べます。

1
@Stick-あなたがする:data = myDict.get(a_key, default)、そしてdo_work与えられたときに正しいことをするdefault(例えばNone)またはあなたがするif data: do_work(data)。いずれの場合も、必要なルックアップは1つだけです。(if data is not None: ...どちらの場合でも、1回のルックアップのみであり、独自のロジックが引き継ぐ可能性があります。)
14年

1
だから、dict.get()を使うと、現在のプロジェクトでかなりの労力を節約できると判断しました。それは私の行数を減らし、ひどい見た目のtry/except/elseジャンクをきれいにし、コードの読みやすさを全体的に改善しました。以前聞いたことのある貴重な教訓を学びましたが、心を奪うことを怠ることができました。「コードを書きすぎていると感じたら、立ち止まって言語機能に目を向けてください。」ありがとう!!

@Stick-それを聞いてうれしいです:)私はそのアドバイスが好きです、私はそれを前に聞いたことがない
確実に14

1
技術的には、どれが最速ですか?
オリビエポンズ

4

不足しているキーはルールまたは例外ですか?

実用的な観点から、スロー/キャッチは、キーがテーブルにあるかどうかをチェックするよりもはるかに遅くなります。キーが見つからないことがよくある場合は、条件を使用する必要があります。

条件を支持するもう1つの要素はelseです。いずれかのブロックが実行された場合、tryブロック内の複数のステートメントから同じ例外クラスがスローされる可能性があることを考慮してください。

except節のコードは通常の欠陥の一部であってはなりません(たとえば、キーが存在しない場合は追加します)-エラーを処理する必要があります。


4
「実用的な観点から、スロー/キャッチは、キーがテーブルにあるかどうかをチェックするよりもはるかに遅い」-いいえ、そうではありません。とにかく、必ずしもそうではありません。最後に確認したとおり、最も一般的なPython実装はtry/ catchバージョンをより高速に実行します。
確実に14年

2
Pythonでのスロー/キャッチは、Pythonでフロー制御によく使用される程度まで、それほど大きなパフォーマンスではありません。さらに、一部の状況では、テストとアクセスの間にその辞書が変更される可能性があります。
whatsisname 14年

@whatsisname-私はそれを私の答えに追加することを考えましたが、TBHにそのような人種の危険がある場合、EAFP vs LBYL:P
14年

1

「pythonic」であり、具体的にはアヒルのタイピングであるという精神で、try / exceptはほとんど壊れやすいように見えます。

本当に必要なのは、dictのようなアクセスを備えた__getitem__ものだけですが、期待どおりではない何かをするためにオーバーライドできます。たとえばdefaultdict、標準ライブラリからの使用:

In [1]: from collections import defaultdict

In [2]: foo = defaultdict(int)

In [3]: foo['a'] = 1

In [4]: foo['b']  # Someone did access checking with a try/except exactly as you have in the question
Out[4]: 0

In [5]: 'a' in foo
Out[5]: True

In [6]: 'b' in foo  # We never set it, but now it exists!
Out[6]: True

In [7]: 'c' in foo
Out[7]: False

デフォルトの戻り値はFalsyであるため、そのようなアクセスチェックはほとんどの場合正常に機能します。コードもシンプルに保つように見えます。それが同僚と私が何ヶ月もやってきた理由です。

残念なことに、数か月後、これらの追加キーは実際には問題と追跡困難なバグを引き起こし0ました。これは有効な値になったためです。また、キーが存在するかどうかを確認するために使用inすることもあります。これにより、コードが同一であるはずだったときに、コードが異なる動作をするようになります。

私がそれが壊れているように見える理由は、Pythonのダックタイピングの精神では、必要なのは辞書のようなものであり、defaultdict継承するオブジェクトを作成する可能性があるように、その要件に完全に適合するからdictです。呼び出し側が後で実装を変更しないことを保証することはできません。そのため、副作用を最小限に抑える必要があります。

最初のバージョンを使用しif myKey in mydictます。


また、これがあることに注意して、具体的問題の例について。「許可よりも許しを求める方が良い」というPythonの信条は、主に、あなたが望むものを手に入れないとき、あなたが実際に正しく行動することを確実にすることを意図しています。たとえば、ファイルが存在するかどうかを確認してからそれを読み取ろうとすると、少なくとも3つのことが頭の外で思い浮かびます。

  • 競合状態とは、ファイルが削除/移動/などされた可能性があることです。
  • ファイルの読み取り権限がありません
  • ファイルが破損しています

最初の「許可を求める」ことはできますが、競合状態の性質上、必ずしも常に機能するとは限りません。2番目のものは、確認するのを忘れるには簡単すぎる。3番目のファイルは、ファイルを読み取ろうとしない限りチェックできません。いずれにせよ、失敗した後に行うことはほぼ確実に同じであるため、この例で、try / exceptを使用して「許しを求める」ことをお勧めします。

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