回答:
これはあなたが望むことを行い、ほとんどすべての場合に機能します:
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
True
'a','b' in ['b', 'a', 'foo', 'bar']Pythonは式をタプルとして解釈するため、式は期待どおりに機能しません。
>>> 'a', 'b'
('a', 'b')
>>> 'a', 5 + 2
('a', 7)
>>> 'a', 'x' in 'xerxes'
('a', True)
このテストを実行する方法は他にもありますが、さまざまな種類の入力に対しては機能しません。以下のようKabieが指摘する、あなたはセットを使用してこの問題を解決することができます...
>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar']))
True
>>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'}
True
...時々:
>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
セットはハッシュ可能な要素でのみ作成できます。しかし、ジェネレータ式all(x in container for x in items)はほとんどすべてのコンテナタイプを処理できます。唯一の要件は、container反復可能であることです(つまり、ジェネレーターではありません)。items反復可能にすることができます。
>>> container = [['b'], 'a', 'foo', 'bar']
>>> items = (i for i in ('a', ['b']))
>>> all(x in [['b'], 'a', 'foo', 'bar'] for x in items)
True
多くの場合、サブセットテストはよりも高速ですallが、違いは衝撃的ではありません-セットがオプションではないために質問が無関係である場合を除いて。このようなテストのためだけにリストをセットに変換することは、常に問題を起こす価値はありません。また、ジェネレータをセットに変換すると、非常に無駄が多くなり、プログラムの実行速度が大幅に低下する場合があります。
ここに、説明のためのいくつかのベンチマークがあります。最大の違いは、両方containerとitemsが比較的小さい場合です。その場合、サブセットアプローチは約1桁速くなります。
>>> smallset = set(range(10))
>>> smallsubset = set(range(5))
>>> %timeit smallset >= smallsubset
110 ns ± 0.702 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> %timeit all(x in smallset for x in smallsubset)
951 ns ± 11.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
これは大きな違いのようです。しかしcontainer、セットである限りall、非常に大きなスケールでも完全に使用できます。
>>> bigset = set(range(100000))
>>> bigsubset = set(range(50000))
>>> %timeit bigset >= bigsubset
1.14 ms ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit all(x in bigset for x in bigsubset)
5.96 ms ± 37 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
サブセットテストの使用はさらに高速ですが、この規模では約5倍です。速度の向上はPythonのの高速cな実装によるものですsetが、基本的なアルゴリズムはどちらの場合も同じです。
あなたがいる場合items、すでに他の理由のためのリストに格納され、その後は、サブセットテストアプローチを使用する前に、セットに変換する必要があります。その後、スピードアップは約2.5倍に低下します。
>>> %timeit bigset >= set(bigsubseq)
2.1 ms ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
そして、あなたcontainerがシーケンスであり、最初に変換する必要がある場合、スピードアップはさらに小さくなります:
>>> %timeit set(bigseq) >= set(bigsubseq)
4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
結果がcontainer非常に遅くなるのは、シーケンスとして終了するときだけです。
>>> %timeit all(x in bigseq for x in bigsubseq)
184 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
そしてもちろん、必要な場合にのみそれを行います。のすべてのアイテムbigseqがハッシュ可能である場合は、代わりに次のようにします。
>>> %timeit bigset = set(bigseq); all(x in bigset for x in bigsubseq)
7.24 ms ± 78 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
これは他の方法よりも1.66倍高速です(set(bigseq) >= set(bigsubseq)上記の4.36の時間)。
そのため、サブセットテストは一般的に高速ですが、信じられないほどのマージンではありません。一方、どちらallが速いか見てみましょう。items1000万の値が長く、値が含まれていない可能性が高い場合はどうなりcontainerますか?
>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); set(bigset) >= set(hugeiter)
13.1 s ± 167 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); all(x in bigset for x in hugeiter)
2.33 ms ± 65.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
この場合、ジェネレーターをセットに変換するのは非常に無駄です。setコンストラクタは、発電機全体を消費する必要があります。しかし、の短絡動作allにより、発電機のごく一部のみを消費する必要があることが保証されるため、サブセットテストよりも4桁速くなります。
これは確かに極端な例です。しかし、それが示すように、どちらのアプローチもすべてのケースでより高速になるとは限りません。
ほとんどのcontainer場合、セットへの変換は、少なくともそのすべての要素がハッシュ可能であれば、価値があります。これinは、セットの場合はO(1)、inシーケンスの場合はO(n)であるためです。
一方、サブセットテストの使用は、たぶんそれだけの価値があるでしょう。テスト項目が既にセットに格納されている場合は、必ずそれを行ってください。それ以外の場合allは、少しだけ遅くなり、追加のストレージは必要ありません。また、アイテムの大規模なジェネレーターで使用することもでき、その場合には大幅なスピードアップが提供されます。
それを行う別の方法:
>>> set(['a','b']).issubset( ['b','a','foo','bar'] )
True
{'a', 'b'} <= {'b','a','foo','bar'}
入力の一致をすべて確認する場合は、
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
少なくとも1つの一致を確認する場合は、
>>> any(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
Pythonパーサーはそのステートメントをタプルとして評価しました。最初の値は'a'で、2番目の値は式です'b' in ['b', 'a', 'foo', 'bar'](これはに評価されますTrue)。
ただし、簡単な関数を記述して、必要な処理を実行できます。
def all_in(candidates, sequence):
for element in candidates:
if element not in sequence:
return False
return True
そしてそれを次のように呼び出します:
>>> all_in(('a', 'b'), ['b', 'a', 'foo', 'bar'])
True
[x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]
これが選択した回答よりも優れていると思う理由は、「all()」関数を呼び出す必要がないためです。空のリストはIFステートメントでFalseと評価され、空でないリストはTrueと評価されます。
if [x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]:
...Do something...
例:
>>> [x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]
['a', 'b']
>>> [x for x in ['G','F'] if x in ['b', 'a', 'foo', 'bar']]
[]
ここで提示する回答は両方とも、繰り返される要素を処理しません。たとえば、[1,2,2]が[1,2,3,4]のサブリストであるかどうかをテストしている場合、どちらもTrueを返します。それはあなたがやろうとしていることかもしれませんが、私は明確にしたかっただけです。[1,2,3,4]の[1,2,2]に対してfalseを返す場合は、両方のリストを並べ替え、各リストで移動インデックスを使用して各アイテムを確認する必要があります。もう少し複雑なforループ。
どうすればラムダなしでpythonicになることができますか?..真剣に受け取られるべきではない..しかし、この方法も機能します:
orig_array = [ ..... ]
test_array = [ ... ]
filter(lambda x:x in test_array, orig_array) == test_array
配列に値があるかどうかをテストする場合は、最後の部分を省略します。
filter(lambda x:x in test_array, orig_array)
filterは、ジェネレータがであるPython 3では意図したとおりに機能しないことを示しています。list実際に、==またはブールコンテキストでテストできる結果を取得したい場合は、それをラップする必要があります(それが空かどうかを確認するため)。リスト内包やジェネレータ式を使用して、anyまたはall望ましいです。
set(['a', 'b']) <= set(['b','a','foo','bar'])同じことを綴る別の方法であり、「より数学的に」見える。