web-dev-qa-db-ja.com

「if key in dict」と「try / except」-より読みやすいイディオムはどれですか?

イディオムと読みやすさについて質問がありますが、この特定のケースではPython哲学の衝突:

辞書Bから辞書Aを作成します。特定のキーがBに存在しない場合は、何もせずに続行します。

どちらの方法が良いですか?

try:
    A["blah"] = B["blah"]
except KeyError:
    pass

または

if "blah" in B:
    A["blah"] = B["blah"]

「許しを求めて」対「単純さと明確さ」。

どちらが良いですか?

84
LeeMobile

例外は条件付きではありません。

条件付きバージョンはより明確です。それは当然です。これは単純なフロー制御であり、例外ではなく条件式が設計されています。

例外バージョンは、主にループでこれらのルックアップを行うときの最適化として使用されます。一部のアルゴリズムでは、内部ループからテストを削除できます。ここにはその利点はありません。 "blah"を2回言う必要がないという小さな利点がありますが、これらの多くを行う場合は、おそらくヘルパーmove_key関数が必要です。

一般的に、特別な理由がない限り、デフォルトで条件付きバージョンを使用することを強くお勧めします。条件はこれを行うための明らかな方法であり、通常、あるソリューションを別のソリューションよりも優先することを強くお勧めします。

64
Glenn Maynard

例外とダブルルックアップの両方を回避する3番目の方法もあります。これは、ルックアップが高価な場合に重要になる可能性があります。

_value = B.get("blah", None)
if value is None: 
    A["blah"] = value
_

辞書にNone値が含まれると予想される場合は、NotImplementedEllipsisなどの難解な定数を使用するか、新しい定数を作成できます。

_MyConst = object()
def update_key(A, B, key):
    value = B.get(key, MyConst)
    if value is not MyConst: 
        A[key] = value
_

とにかく、update()を使用することが私にとって最も読みやすいオプションです:

_a.update((k, b[k]) for k in ("foo", "bar", "blah") if k in b)
_
49
lqc

私が理解していることから、dict Bからのキーと値のペアでdict Aを更新したい

updateがより良い選択です。

A.update(B)

例:

>>> A = {'a':1, 'b': 2, 'c':3}
>>> B = {'d': 2, 'b':5, 'c': 4}
>>> A.update(B)
>>> A
{'a': 1, 'c': 4, 'b': 5, 'd': 2}
>>> 
14
pyfunc

Pythonパフォーマンスwikiからの直接引用:

初めて例外を除き、Wordが表示されるたびにifステートメントのテストが失敗します。多数の単語をカウントしている場合、多くの単語がおそらく複数回出現します。値の初期化が1回だけ発生し、その値の増加が何度も発生する状況では、tryステートメントを使用する方が安価です。

したがって、状況に応じて両方のオプションが実行可能であるようです。詳細については、次のリンクをご覧ください。 Try-except-performance

7
Sami Lehtinen

ここでの一般的なルールはA["blah"]は通常存在します。そうであれば、try-exceptが適切でない場合は、if "blah" in b:

「トライ」は時間的には安いと思いますが、「除外」はもっと高価です。

3
neil

2番目の例は、このコードが理にかなっていない場合に使用すべきものだと思います。

try:
    A["foo"] = B["foo"]
    A["bar"] = B["bar"]
    A["baz"] = B["baz"]
except KeyError:
    pass

Bにないキーがあるとすぐにコードが中断することに注意してください。このコードが理にかなっている場合は、例外メソッドを使用する必要があります。そうでない場合は、テストメソッドを使用します。私の意見では、より短く、意図を明確に表現しているため、例外メソッドよりも読みやすくなっています。

もちろん、updateを使用するように言っている人は正しいです。辞書の内包表記をサポートするPythonのバージョンを使用している場合、このコードを強くお勧めします。

updateset = {'foo', 'bar', 'baz'}
A.update({k: B[k] for k in updateset if k in B})
3
Omnifarious

他の言語のルールは、例外的な条件、つまり通常の使用では発生しないエラーの例外を予約することです。 StopIterationはその規則によって存在すべきではないので、その規則がPythonにどのように適用されるのかわかりません。

2
Mark Ransom

個人的には、2番目の方法に傾いています(ただし、has_key):

if B.has_key("blah"):
  A["blah"] = B["blah"]

このように、各割り当て操作は2行(try/exceptを使用した4行ではなく)であり、スローされる例外は実際のエラーまたは見落としたものになります(存在しないキーにアクセスしようとするのではなく) 。

判明したように(質問のコメントを参照)、has_keyは非推奨です。したがって、

if "blah" in B:
  A["blah"] = B["blah"]
1
girasquid

_Python 3.8_を開始し、 代入式(PEP 572) (_:=_演算子)を導入すると、変数valueの条件値dictB.get('hello', None)を順番にキャプチャできます(dict.get('hello', None)が関連する値またはNoneを返すため)Noneではないかどうかを確認し、条件の本文内で使用します。

_# dictB = {'hello': 5, 'world': 42}
# dictA = {}
if value := dictB.get('hello', None):
  dictA["hello"] = value
# dictA is now {'hello': 5}
_
1
Xavier Guihot

なぜこれをしないのですか:

def try_except(x,col):
    try:
        return x[col]
    except:
        return None

list(map(lambda x: try_except(x,'blah'),A))
0
Nico Coallier