如何测试列表中多个值的成员资格

2025-01-10 08:47:00
admin
原创
99
摘要:问题描述:我想测试两个或多个值是否属于列表成员,但是得到了意外的结果:>>> 'a','b' in ['b', 'a', 'foo', 'bar'] ('a', True) 那么,Python 能否一次测试列表中多个值的成员资格?结果意味着什么?另请参阅:如何查找列表交集?。检查列表中是否有...

问题描述:

我想测试两个或多个值是否属于列表成员,但是得到了意外的结果:

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

那么,Python 能否一次测试列表中多个值的成员资格?结果意味着什么?


另请参阅:如何查找列表交集?。检查列表中是否有任何指定的值,相当于检查交集是否非空。检查列表中是否包含所有值,相当于检查它们是否是子集。


解决方案 1:

这将满足您的要求,并且几乎在所有情况下都有效:

>>> 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相对较小时。在这种情况下,子集方法的速度大约快一个数量级:

>>> 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更快。如果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确保只需要消耗生成器的一小部分,因此它比子集测试快四个数量级

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

结果

大多数情况下,转换container为集合是值得的,至少如果其所有元素都是可哈希的。这是因为in集合的复杂度为 O(1),而in序列的复杂度为 O(n)。

另一方面,使用子集测试可能只是有时值得。如果您的测试项目已经存储在一个集合中,那么一定要这样做。否则,all只会慢一点,并且不需要任何额外的存储空间。它也可以用于大型项目生成器,有时在这种情况下可以提供巨大的加速。

解决方案 2:

另一种方法:

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

解决方案 3:

如果你想检查所有输入匹配项

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

如果你想检查至少一个匹配项

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

解决方案 4:

我很确定in具有比更高的优先级,,因此您的语句被解释为'a', ('b' in ['b' ...]),然后计算为,'a', True因为'b'在数组中。

请参阅前面的答案以了解如何做你想做的事情。

解决方案 5:

Python 解析器将该语句作为元组进行评估,其中第一个值为'a',第二个值是表达式'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

解决方案 6:

我想说我们甚至可以省略那些方括号。

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

解决方案 7:

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

解决方案 8:

这里给出的两个答案都不会处理重复元素。例如,如果您正在测试 [1,2,2] 是否是 [1,2,3,4] 的子列表,则两者都将返回 True。那可能是您想要做的,但我只是想澄清一下。如果您想对 [1,2,2] in [1,2,3,4] 返回 false,您需要对两个列表进行排序,并使用每个列表上的移动索引检查每个项目。只是一个稍微复杂一点的 for 循环。

解决方案 9:

任何

在 Python3 中,你可以使用集合的交集作为any

>>> {'a','b'} & set(['b', 'a', 'foo', 'bar'])
{'a', 'b'}

>>> {'a','b'} & set(['b', 1, 'foo', 'bar'])
{'b'}

当然,你可以将结果包装在布尔值TrueFalse

>>> bool({'a','b'} & set(['b', 1, 'foo', 'bar']))
True

>>> bool({'c'} & set(['b', 1, 'foo', 'bar']))
False

全部

利用issubset

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

>>> {'a','b'}.issubset(set(['b', 1, 'foo', 'bar']))
False

笔记

  • bool()将集合转换为布尔值

  • issubset()寻找一个集合是另一个集合的完整子集

  • &可以用于集合的交集(任意)

示例的更简洁版本,使用变量:

# Variable setup:
test = {'a','b'}
values = set(['b', 'a', 'foo', 'bar'])

# Example: Any
test & values         # {'a', 'b'}
bool(test & values)   # True

# Example: All
test.issubset(values) # True

解决方案 10:

以下是我的做法:

A = ['a','b','c']
B = ['c']
logic = [(x in B) for x in A]
if True in logic:
    do something

解决方案 11:

没有 lambdas 你怎么能称得上是 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)

解决方案 12:

# This is to extract all count of all combinations inside list of 
# list
import itertools

l = [[1,2,3],[6,5,4,3,7,2],[4,3,2,9],[6,7],[5,1,0],[6,3,2,7]]    
els = list(set(b for a in l for b in a))    
sol = {}    

def valid(p):    
    for s in l:    
        if set(p).issubset(set(s)):    
            if p in sol.keys():    
                sol[p] += 1    
            else:    
                sol[p] = 1    

for c in itertools.combinations(els, 2):    
    valid(c)  
# {(0, 1): 1, 
# (0, 5): 1,
# (1, 2): 1,
# (1, 3): 1,
# (1, 5): 1,
# (2, 3): 4,
# (2, 4): 2,
# (2, 5): 1,
# (2, 6): 2,
# (2, 7): 2,
# (2, 9): 1,
# (3, 4): 2,
# (3, 5): 1,
# (3, 6): 2,
# (3, 7): 2,
# (3, 9): 1,
# (4, 5): 1,
# (4, 6): 1,
# (4, 7): 1,
# (4, 9): 1,
# (5, 6): 1,
# (5, 7): 1,
# (6, 7): 3}   
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用