Pythonのリストに何かがある(ない)かどうかを確認する


314

Pythonにタプルのリストがあり、タプルがリストにない場合にのみ分岐を実行する条件があります(リストにある場合、if分岐は実行しません)

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

    # Do Something

これは本当に私のために働いていません。何が悪いのでしょうか?


1
3 -1 > 0 and (4-1 , 5) not in []Trueしたがって、エラーは演算子の優先順位の1つではないことに注意してください。
Dan D.

6
「本当に私のために働いていない」とはどういう意味ですか?何が起こると思いますか?実際にはどうなりますか?リストの正確な内容によって、問題が発生しますか?
Karl Knechtel、2012年

しようとしないのはなぜmyList.count((curr_x, curr_y))ならば、(curr_x, curr_y)ではないがmyList、結果はとなります0
LittleLittleQ


2
この「私のコードは実際には機能していません」という質問はどうして297票を得たのですか?最小限の再現可能な例を教えてください。
ヘリット・

回答:


503

バグはおそらくコード内の別の場所にあります。問題なく機能するはずです。

>>> 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

11
@ザック:このことを知らなかったとしても、あなたはそうすることができますif not ELEMENT in COLLECTION:
ninjagecko

@ninjagecko:効率が悪い、または正しくない可能性のあるコンテナーのタイプによって異なります。たとえば、ブルームフィルターを参照してください。
orlp

14
@nightcracker A not in B減らさnot B.__contains__(A)れたものnot A in Bと同じですnot B.__contains__(A)
Dan D.

1
ああ、すごい、Pythonがのようなものだったと誓ったかもしれない__notcontains__。申し訳ありませんが、私が言ったことはでたらめです。
orlp

2
@ std''OrgnlDave起こり得る唯一の方法は、そうでない場合notよりも優先順位が高い場合ですin。その結果を検討ast.dump(ast.parse("not A in B").body[0])中の結果どの"Expr(value=UnaryOp(op=Not(), operand=Compare(left=Name(id='A', ctx=Load()), ops=[In()], comparators=[Name(id='B', ctx=Load())])))"場合には、notAにしっかりとグループ化し、一つがあることを、結果を期待していた"Expr(value=Compare(left=UnaryOp(op=Not(), operand=Name(id='A', ctx=Load())), ops=[In()], comparators=[Name(id='B', ctx=Load())]))"ため、解析しています"(not A) in B"
Dan D.

20

Pythonのリストに何かがあるかどうかを確認するにはどうすればよいですか?

最も安価で読みやすいソリューションは、in演算子(または特定のケースではnot in)を使用することです。ドキュメントで述べたように、

演算子innot inメンバーシップをテストします。がのメンバーであるかどうかをx in s評価し、それ以外のTrue場合xは評価し ます。の否定を返します。sFalsex 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アイテムのインデックスを取得するために使用できます。その要素が存在しない場合、a ValueErrorが発生します。
  • list.count 出現回数を数えたい場合に使用できます。

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

次の質問を自問してください。

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

これらの質問に「はい」と答えた場合は、set代わりにを使用する必要があります。s のinメンバーシップテスト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)

注意として、これは、格納および検索している要素がハッシュ可能である限り、適切なオプションです。IOW、それらは不変の型か、を実装するオブジェクトでなければなりません__hash__


2
セットは常にオプションであるとは限りません(たとえば、変更可能なアイテムのリストがある場合)。大規模なコレクションの場合:とにかく、ルックアップ用のセットの構築はO(n)時間であり、メモリ使用量を倍増させる可能性があります。まだルックアップがない場合は、ルックアップを作成/維持することが常に最良の選択であるとは限りません。
WIM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.