Python dictで「has_key()」または「in」を使用する必要がありますか?


911

私は何をする方が良いのでしょうか:

d = {'a': 1, 'b': 2}
'a' in d
True

または:

d = {'a': 1, 'b': 2}
d.has_key('a')
True

回答:


1287

in 間違いなくもっとpythonicです。

実際has_key()、Python 3.xで削除されました


3
追加として、パイソン3に、代わりのキーのうち、値が存在するかどうかを確認するために、(d.valuesに>>> 1を試してください)
リザ

217
ただし、避けるべきことは、「key in some_dict.keys()」ではなく「key in some_dict」を実行することです。どちらも意味的には同等ですが、パフォーマンスに関しては後者の方がはるかに遅くなります(O(n)対O(1))。私は、人々が「in dict.keys()」を実行することを見て、それがより明示的であり、したがってより優れていると考えています。
アダムパーキン

2
@AdamParkin私は私の回答であなたのコメントを示しましたstackoverflow.com/a/41390975/117471
Bruno Bronosky

8
@AdamParkin Python 3ではkeys()、コピーではなくディクショナリへのセットに似たビューなのでx in d.keys()、O(1)も同様です。それでも、x in dよりPythonicです。
アーサータッカ

2
@AdamParkin興味深いことに、私はそれを見ませんでした。x in d.keys()それは、x in d.keys()算術演算(ハッシュの計算)を実行し、ルックアップを実行するだけの一時的なオブジェクトを作成して破棄する必要があるためです。これd.keys()は約10倍の長さにすぎないことに注意してください。私はチェックしていませんが、それがO(1)だけであると確信しています。
Arthur Tacca

253

in 優雅なだけでなく(そして非推奨ではない)だけでなく、パフォーマンスでもハンズダウンで勝利します。例:

$ python -mtimeit -s'd=dict.fromkeys(range(99))' '12 in d'
10000000 loops, best of 3: 0.0983 usec per loop
$ python -mtimeit -s'd=dict.fromkeys(range(99))' 'd.has_key(12)'
1000000 loops, best of 3: 0.21 usec per loop

次の観察は常に正しいとは限りません通常、Pythonでは高速なソリューションの方がエレガントでPythonicであることがわかります。それ-mtimeitがとても役立つ理由です-それはあちこちに100ナノ秒を節約するだけではありません!-)


4
このおかげで、「in some_dict」が実際にO(1)であることを確認しやすくなりました(99を増やして1999とすると、ランタイムはほぼ同じであることがわかります)。
Adam Parkin、

2
has_keyO(1)のようにも見えます。
dan-gph 2015年


42

使用dict.has_key()あなたのコードは、以前の(2.3よりもPythonのバージョンで実行可能であることが必要である(および場合のみ)場合key in dict導入されました)。


1
2013年のWebSphereアップデートでは、Jython 2.1をメインのスクリプト言語として使用しています。ですから、これは、残念ながら、覚えてから5年後のことです。
ArtOfWarfare 2014

23

in実際にパフォーマンスを低下させる1つの例があります。

あなたが使用している場合はinO(1)コンテナにのみ実装__getitem__し、has_key()が、ではない__contains__(とあなたはO(1)O(N)の検索に検索を向けるだろうin経由で線形検索にフォールバック__getitem__)。

修正は明らかに簡単です:

def __contains__(self, x):
    return self.has_key(x)

6
この回答は投稿時に適用されましたが、99.95%の読者が無視しても問題ありません。では、ほとんどの場合、あなたは、これはあなたがそれを知っているよ不明瞭なもので作業している場合。
wizzwizz4

2
これは本当に問題ではありません。has_key()あるPythonの2の辞書に固有in/ __contains__は使用する正しいAPIです。フルスキャンが避けられないコンテナの場合、いずれにしてもhas_key()メソッドはありません。O(1)のアプローチがある場合、それはユースケース固有であり、開発者が問題に適したデータ型を選択する必要があります。
Martijn Pieters

15

has_keyはディクショナリメソッドですがin、任意のコレクションで機能し、__contains__見つからないin場合でも、他のメソッドを使用してコレクションを反復処理して調べます。


1
イテレータ "x in xrange(90、200)<=> 90 <= x <200"でも機能します
u0b34a0f6ae 2009

1
...:非常に悪い考えのようなこのルックス:50回の操作の代わりに2
クレマン

1
@ClémentPython 3では、オブジェクトのinテストを行うことは実際には非常に効率的rangeです。xrangeただし、Python 2 での効率についてはよくわかりません。;)
PM 2Ring

@ClémentはPython 3にはありません。値が範囲内かどうか__contains__を簡単に計算できます。
Martijn Pieters

1
@AlexandreHuatタイミングには、range毎回新しいインスタンスを作成するオーバーヘッドが含まれます。単一の既存のインスタンスを使用すると、「範囲内の整数」テストは私のタイミングで約40%速くなります。
MisterMiyagi

14

dict.has_key()のソリューションは非推奨です。「in」を使用してください-崇高なテキストエディター3

ここでは、「年齢」という名前の辞書の例を取り上げました-

ages = {}

# Add a couple of names to the dictionary
ages['Sue'] = 23

ages['Peter'] = 19

ages['Andrew'] = 78

ages['Karren'] = 45

# use of 'in' in if condition instead of function_name.has_key(key-name).
if 'Sue' in ages:

    print "Sue is in the dictionary. She is", ages['Sue'], "years old"

else:

    print "Sue is not in the dictionary"

6
正解ですが、すでに回答されています。Stackoveflowへようこそ。例のおかげで、常に回答を確認してください。
イゴールグ

@igorgue私は彼女への反対票についてはよくわかりません。彼女の答えはすでに答えたものと似ているかもしれませんが、彼女は例を提供します。SOの答えになるほどの価値はありませんか?
Akshat Agarwal 2016

14

アダム・パーキンのコメントでアレックス・マーテリのパフォーマンステストを拡大しています...

$ python3.5 -mtimeit -s'd=dict.fromkeys(range( 99))' 'd.has_key(12)'
Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/timeit.py", line 301, in main
    x = t.timeit(number)
  File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/timeit.py", line 178, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
    d.has_key(12)
AttributeError: 'dict' object has no attribute 'has_key'

$ python2.7 -mtimeit -s'd=dict.fromkeys(range(  99))' 'd.has_key(12)'
10000000 loops, best of 3: 0.0872 usec per loop

$ python2.7 -mtimeit -s'd=dict.fromkeys(range(1999))' 'd.has_key(12)'
10000000 loops, best of 3: 0.0858 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(  99))' '12 in d'
10000000 loops, best of 3: 0.031 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(1999))' '12 in d'
10000000 loops, best of 3: 0.033 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(  99))' '12 in d.keys()'
10000000 loops, best of 3: 0.115 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(1999))' '12 in d.keys()'
10000000 loops, best of 3: 0.117 usec per loop

素晴らしい統計、時には暗黙的は明示的(少なくとも効率的に)よりも良いかもしれません...
varun

ありがとう、@ varun。私はこの答えを忘れていました。この種のテストをもっと頻繁に行う必要があります。私は、人々がThe Best Way™について議論する長いスレッドを定期的に読んでいます。しかし、これが証明を得るのがいかに簡単であったかは、ほとんど覚えていません。
Bruno Bronosky

0

このようなものがあれば:

t.has_key(ew)

Python 3.X以降で実行するには、以下に変更します。

key = ew
if key not in t

6
いいえ、テストを逆にしました。値参照がディクショナリのキーでもある場合にt.has_key(ew)返されます。値が辞書にない場合に返されます。さらに、エイリアスは非常に冗長です。正しいスペルはです。これは、8年前に受け入れられた回答がすでにあなたに言ったことです。Trueewkey not in tTruekey = ewif ew in t
Martijn Pieters
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.