检查两个给定列表中是否存在元素的最简单,最优雅的方法是什么?例如,我有两个列表如下?
>>>a, b = ['a', 'b', 'g', 'r'], ['e', 'g', 'l', 1, 'w']
现在在上面给出的列表中,我想检查两个列表中是否存在任何元素。目前我正在做如下
def elm_exists(list_a, list_b):
for elm in list_a:
if elm in list_b:
return True
return False
有更优雅的方式吗?
答案 0 :(得分:12)
使用以下内容检查a
和b
的元素:
set(a).intersection(b)
示例:
In [44]: nk=set(a).intersection(b)
In [45]: for x in a:
...: if x in nk:
...: print x, 'present in b'
...: else:
...: print x, 'absent in b'
...:
a absent in b
b absent in b
g present in b
r absent in b
也:
In [47]: timeit(set(a) & set(b))
1000000 loops, best of 3: 940 ns per loop
In [48]: timeit(set(a).intersection(b))
1000000 loops, best of 3: 854 ns per loop
In [49]: timeit([x for x in a if x in b])
1000000 loops, best of 3: 1 us per loop
因此使用set(a).intersection(b)
。
答案 1 :(得分:7)
这有效:
>>> l1,l2=['a', 'b', 'g', 'r'], ['e', 'g', 'l', 1, 'w']
>>> list(set(l1)&set(l2))
['g']
答案 2 :(得分:3)
您无需将list
转换为set
,只需转换为{1}。我认为跳过不必要的转换会使其更具可读性和优雅性。
所以,要么:
set(a).intersection(b)
或者:
s = set(a)
any(e in s for e in b)
后者具有短路的优势,只要找到一个匹配,并更好地表达逻辑,并返回True
或False
而不是非假的或假的{{1如果这困扰你,那么它是两行而不是一行。我不知道这个优势是否取消了将循环置于生成器表达式而不是C函数内的成本。
这个小set
的性能结果几乎毫无意义,所以让我们试试这个:
list
要将数字全部放在纳秒级别中,请加上In [373]: a=[random.choice(string.ascii_lowercase) for _ in range(10000)]
In [374]: b=[random.choice(string.ascii_lowercase) for _ in range(10000)]
In [375]: %timeit(set(a))
10000 loops, best of 3: 180 us per loop
In [376]: s=set(a) # since all versions need to do this
In [391]: %timeit(s & set(b))
1000000 loops, best of 3: 178 us per loop
In [392]: %timeit(s.intersection(b))
1000000 loops, best of 3: 247 us per loop
In [393]: %timeit(discard(e in s for e in b))
1000000 loops, best of 3: 550 ns per loop
In [394]: %timeit(any(e in s for e in b))
1000000 loops, best of 3: 749 ns per loop
In [395]: %timeit(any(e in a for e in b))
1000000 loops, best of 3: 1.42 us per loop
除最后需要之外的所有成本,并比较三个Python版本中的相同测试(Apple stock CPython 2.7.2,python .org CPython 3.3.0,Homebrew PyPy 1.9.0 / 2.7.2,所有64位Mac版本):
set(a)
现在我想起来,这完全有道理。很早就发生碰撞的可能性非常高,因此将整个事物转换成一套的成本占主导地位。
这意味着我们需要一个具有10000个唯一值的新测试。让我们重复一下这个测试:
CP272 CP330 PyPy
s & set(b) 358000 316000 180500
s.intersection(b) 427000 459000 180900
discard(genexp) 180550 157341 90094
any(genexp) 180749 157334 90208
any(list-genexp) 1420 686 960
这些更合理。他们仍然有道理。如果您正在比较随机洗牌的相同10000个元素,那么您需要走多远?不足以支付In [29]: a, b = list(range(10000)), list(range(10000))
In [30]: random.shuffle(a)
In [31]: random.shuffle(b)
CP272 CP330 PyPy
s & set(b) 1277000 1168000 1141000
s.intersection(b) 1165000 1117000 2520000
discard(genexp) 1699000 1271000 770000
any(genexp) 389800 344543 320807
any(list-genexp) 62000 10400 1520
的费用 - 如果其中任何一个列表值得做,更不用说两者了!
所以,让我们尝试一下 no 匹配的情况:
set
我不知道PyPy如何快速完成最后一个,但除此之外,这里没有任何惊喜。
那么哪一个最好?
显然,如果你期望发生很多碰撞,你想尽可能避免制作套装 - 但是如果你预计碰撞很少,你想要制作至少一套。如果你不知道,我认为最安全的赌注是In [43]: a=list(range(10000, 20000))
CP272 CP330 PyPy
s & set(b) 751000 770000 733000
s.intersection(b) 466000 530000 1920000
discard(genexp) 1246000 985000 749000
any(genexp) 1269000 966000 893000
any(list-genexp) 185000000 176000000 5870000
- 最糟糕的是它不到最好的3倍,如果碰撞率很高,它会快得多。但你可以看一下这些数字并亲自看看。
或者,当然,更好的是,将它们全部与您期望遇到的真实测试数据进行对比。
答案 3 :(得分:1)
def elm_exists(lista, listb):
return bool(set(lista) & set(listb))
对于所有非空容器,bool
函数将返回True
,对于空容器,False
将返回&
。集合交集(set(lista).intersection(listb)
)将返回两组的一组公共元素。请注意,集合将删除任何重复项。
或者,您可以在bool
函数中使用{{1}}。
答案 4 :(得分:0)
>>> y = [1,23,3]
>>> z = [3,432]
>>> (3 in y) and (3 in z)
True
答案 5 :(得分:0)
这是另一种解决方案,
>>> c = [filter(lambda x: x in b, sublist) for sublist in a]
>>> filter (lambda a: a != '', c)
['g']