web-dev-qa-db-ja.com

何かがPythonのリストに含まれているかどうかをチェックする

私は Python にタプルのリストを持っています、そして、もしそのタプルがリストの中になければブランチを取ることを望む条件があります。 ifブランチ)

if curr_x -1 > 0 and (curr_x-1 , curr_y) not in myList: 

    # Do Something

これは私にとって実際にはうまくいきません。私は何をしましたか。

237
Zack

バグはおそらくあなたのコードのどこかにあるでしょう。

>>> 3 not in [2, 3, 4]
False
>>> 3 not in [4, 5, 6]
True

またはタプルを使って:

>>> (2, 3) not in [(2, 3), (5, 6), (9, 1)]
False
>>> (2, 3) not in [(2, 7), (7, 3), "hi"]
True
367
orlp

何かがPythonのリストに含まれている(含まれていない)かどうかをどうやって確認するのですか?

最も安くて読みやすい解決策は in 演算子(あるいはあなたの特定のケースではnot in)を使うことです。ドキュメントに記載されているように、

演算子innot inはメンバーシップをテストします。 Truexのメンバーの場合はx in ssと評価され、それ以外の場合はFalseと評価されます。 x not in sx in sの否定を返します。

さらに、

演算子not inは、逆の真の値inを持つように定義されています。

y not in xは論理的にnot y in xと同じです。

いくつか例を挙げます。

'a' in [1, 2, 3]
# False

'c' in ['a', 'b', 'c']
# True

'a' not in [1, 2, 3]
# True

'c' not in ['a', 'b', 'c']
# False

タプルはハッシュ可能なので(タプルも不変であるという事実の結果として)、これはタプルでも機能します。

(1, 2) in [(3, 4), (1, 2)]
#  True

RHS上のオブジェクトが __contains__() メソッドを定義している場合、ドキュメントの 比較 セクションの最後の段落で説明したように、inは内部的にそれを呼び出します。

... inおよびnot inは、反復可能な型、または__contains__()メソッドを実装する型によってサポートされています。例えば、あなたはこれをすることができます(しかしそうすべきではありません):

[3, 2, 1].__contains__(1)
# True

inはショートサーキットなので、あなたの要素がリストの先頭にある場合、inはより速く評価されます。

lst = list(range(10001))
%timeit 1 in lst
%timeit 10000 in lst  # Expected to take longer time.

68.9 ns ± 0.613 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
178 µs ± 5.01 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

項目がリストに含まれているかどうかを確認するだけでなく、それ以上のことをしたい場合は、次のような選択肢があります。

  • list.indexはアイテムのインデックスを取得するために使用できます。その要素が存在しない場合は、ValueErrorが発生します。
  • 出現回数を数えたい場合はlist.countを使用できます。

XY問題:setsを検討しましたか?

あなた自身にこれらの質問をしてください:

  • アイテムがリストに複数回含まれているかどうかを確認する必要がありますか。
  • このチェックはループの内部で行われますか、それとも関数は繰り返し呼び出されますか?
  • リストに保存しているアイテムはハッシュ可能ですか。さて、あなたは彼らにhashを呼ぶことができますか?

これらの質問に「はい」と答えた場合は、代わりにsetを使用してください。 insのlistメンバーシップテストは、O(n)時間計算量です。これは、pythonがあなたのリストを直線的にスキャンし、各要素を訪問し、それを検索項目と比較しなければならないことを意味します。これを繰り返し実行している場合、またはリストが大きい場合は、この操作によってオーバーヘッドが発生します。

一方、setオブジェクトは、一定時間のメンバーシップチェックのためにそれらの値をハッシュします。チェックはinを使っても行われます。

1 in {1, 2, 3} 
# True

'a' not in {'a', 'b', 'c'}
# False

(1, 2) in {('a', 'c'), (1, 2)}
# True

あなたが探している/探していない要素があなたのリストの最後にあるというのが残念なことであれば、pythonはリストを最後までスキャンしたでしょう。これは以下のタイミングから明らかです。

l = list(range(100001))
s = set(l)

%timeit 100000 in l
%timeit 100000 in s

2.58 ms ± 58.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
101 ns ± 9.53 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

念のために言っておきますが、これはあなたが保存し検索している要素がハッシュ可能である限り適切なオプションです。さて、それらは不変の型か__hash__を実装するオブジェクトでなければならないでしょう。

6
cs95