Python可以测试列表中多个值的成员资格吗?

Noe*_*eto 97 python

我想测试两个或多个值是否在列表中具有成员资格,但我得到了意外的结果:

>>> 'a','b' in ['b', 'a', 'foo', 'bar']
('a', True)
Run Code Online (Sandbox Code Playgroud)

那么,Python可以在列表中一次测试多个值的成员资格吗?这个结果意味着什么?

sen*_*rle 164

这可以满足您的需求,几乎可以在所有情况下使用:

>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
True
Run Code Online (Sandbox Code Playgroud)

该表达式'a','b' in ['b', 'a', 'foo', 'bar']无法按预期工作,因为Python将其解释为元组:

>>> 'a', 'b'
('a', 'b')
>>> 'a', 5 + 2
('a', 7)
>>> 'a', 'x' in 'xerxes'
('a', True)
Run Code Online (Sandbox Code Playgroud)

其他选择

还有其他方法可以执行此测试,但它们不适用于许多不同类型的输入.正如Kabie指出的那样,你可以使用集合解决这个问题......

>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar']))
True
>>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'}
True
Run Code Online (Sandbox Code Playgroud)

...有时:

>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Run Code Online (Sandbox Code Playgroud)

只能使用hashable元素创建集合.但是生成器表达式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
Run Code Online (Sandbox Code Playgroud)

速度测试

在许多情况下,子集测试会比这更快all,但差异并不令人震惊 - 除非问题无关紧要,因为集合不是一种选择.将列表转换为仅用于此类测试的集合并不总是值得的.将发电机转换为集合有时会非常浪费,使程序速度降低许多数量级.

以下是一些基准测试说明.最大的区别来当两个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)
Run Code Online (Sandbox Code Playgroud)

这看起来很不一样.但只要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)
Run Code Online (Sandbox Code Playgroud)

使用子集测试仍然更快,但在这个规模上只有大约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)
Run Code Online (Sandbox Code Playgroud)

如果你container是一个序列,并且需要先转换,那么加速甚至更小:

>>> %timeit set(bigseq) >= set(bigsubseq)
4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Run Code Online (Sandbox Code Playgroud)

我们唯一一次得到灾难性的缓慢结果就是当我们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)
Run Code Online (Sandbox Code Playgroud)

当然,如果必须,我们只会这样做.如果所有项目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)
Run Code Online (Sandbox Code Playgroud)

这比替代方案快1.66倍(set(bigseq) >= set(bigsubseq)时间高于4.36).

因此子集测试通常更快,但不是令人难以置信的余量.另一方面,让我们看看何时all更快.如果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)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,将发电机转换为一组是非常浪费的.该set构造具有消耗整个发电机.但短路行为all确保只需要消耗一小部分发电机,因此它比子集测试快四个数量级.

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

Upshot

大多数情况下,转换container为集合是值得的,至少如果它的所有元素都是可以清除的话.那是因为in对于集合是O(1),而in对于序列是O(n).

另一方面,使用子集测试可能有时值得.如果您的测试项目已存储在集合中,请务必执行此操作.否则,all只会慢一点,并且不需要任何额外的存储空间.它也可以用于大型物品发生器,有时在这种情况下提供大量加速.


Kab*_*bie 57

另一种方法:

>>> set(['a','b']).issubset( ['b','a','foo','bar'] )
True
Run Code Online (Sandbox Code Playgroud)

  • 有趣的事实:`set(['a','b'])<= set(['b','a','foo','bar'])`是拼写相同的东西的另一种方式,看起来"mathier". (19认同)
  • 从Python 2.7开始,你可以使用`{'a','b'} <= {'b','a','foo','bar'}` (7认同)

Moh*_*med 11

如果要检查所有输入匹配项

>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
Run Code Online (Sandbox Code Playgroud)

如果你想检查至少一场比赛

>>> any(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
Run Code Online (Sandbox Code Playgroud)


Foo*_*oon 10

我很确定in优先级高于,你的语句被解释为'a',('b'......中的'b'),然后评估为'a',True,因为'b'是在数组中.

请参阅上一个答案,了解如何做您想做的事.