Python能testing列表中多个值的成员吗?

我想testing两个或更多值是否具有列表上的成员资格,但是我得到一个意外的结果:

>>> 'a','b' in ['b', 'a', 'foo', 'bar'] ('a', True) 

那么,Python能够在列表中一次testing多个值的成员吗? 这个结果是什么意思?

这就是你想要的,几乎可以在所有情况下工作:

 >>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b']) True 

'a','b' in ['b', 'a', 'foo', 'bar']中的expression式'a','b' in ['b', 'a', 'foo', 'bar']不能按预期工作,因为Python将其解释为一个元组:

 >>> 'a', 'b' ('a', 'b') >>> 'a', 5 + 2 ('a', 7) >>> 'a', 'x' in 'xerxes' ('a', True) 

其他选项

还有其他的方法来执行这个testing,但是它们不能用于许多不同types的input。 正如Kabie指出的那样,你可以用set来解决这个问题。

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

集合只能用可哈希元素创build。 但是生成器expression式all(x in container for x in items)几乎可以处理任何容器types。 唯一的要求是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 

速度testing

在许多情况下,子集testing将比alltesting都快,但是这种差异并不令人震惊 – 除非问题不相关,因为集合不是一个选项。 仅仅为了这样的testing目的而将列表转换为集合并不总是值得麻烦的。 而将发电机组转换成电台有时会令人难以置信的浪费,使节目减慢了许多数量级。

这里有一些插图的基准。 当containeritems都相对较小时,最大的区别就在于此。 在这种情况下,子集方法快一个数量级:

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

使用子集testing的速度还是比较快,但是在这个尺度上只有5倍左右。 速度提升是由于Python的快速c backed实现,但基本algorithm在两种情况下是相同的。

如果您的items由于其他原因已经存储在列表中,则在使用子集testing方法之前,必须将它们转换为集合。 然后加速下降到大约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) 

这只是比替代scheme( set(bigseq) >= set(bigsubseq) ,定时在4.36以上)。

因此,子集testing通常更快,但不是一个令人难以置信的边缘。 另一方面,让我们看看什么时候更快。 如果items价值是千万的价值,并且可能具有不在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的短路行为all确保只需要消耗一小部分发电机,因此比子集testing快四个数量级

诚然,这是一个极端的例子。 但是正如它所表明的那样,你不能假设在所有情况下,一种方法或另一种方法会更快。

Upshot

大多数情况下,将container转换为一个集合是值得的,至less如果它的所有元素都是可散列的。 那是因为in集合中是O(1),而in序列中是O(n)。

另一方面,使用子集testing可能仅仅是值得的。 当然,如果你的testing项目已经存储在一个集合中。 否则, all的只是慢一点,并不需要任何额外的存储空间。 它也可以用于大型物品的发生器,有时在这种情况下提供了一个巨大的加速。

另一种方法来做到这一点:

 >>> set(['a','b']).issubset( ['b','a','foo','bar'] ) True 

我敢肯定,你的语句的优先级高于,所以你的语句被解释为'a'('b'in ['b'…]),然后评估为'a',因为'b '在数组中。

看到以前的答案如何做你想要的。

Pythonparsing器将该语句评估为一个元组,其中第一个值为'a' ,第二个值为'b' in ['b', 'a', 'foo', 'bar']的expression式'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 

这里给出的两个答案都不会处理重复的元素。 例如,如果您正在testing[1,2,2]是否为[1,2,3,4]的子列表,则两者都将返回True。 这可能是你的意思,但我只是想澄清。 如果你想在[1,2,3,4]中为[1,2,2]返回false,你将需要对两个列表进行sorting,并检查每个列表上的移动索引。 只是一个稍微复杂的循环。

我会说我们甚至可以把这些方括号留下。

  array = ['b', 'a', 'foo', 'bar'] all([i in array for i in 'a', 'b']) 

PS:我会添加这个作为评论,但我没有足够的代表。

 [x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']] 

我认为这比select的答案更好的原因是你真的不需要调用'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']] [] 

你怎么能pythonic没有lambda! ..不被认真对待..但这种方式也起作用:

 orig_array = [ ..... ] test_array = [ ... ] filter(lambda x:x in test_array, orig_array) == test_array 

如果要testing是否有任何值在数组中,则省略末端部分:

 filter(lambda x:x in test_array, orig_array)