10

检查一个元素是否存在于两个给定列表中的最简单和最优雅的方法是什么。例如,我有两个列表如下?

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

有没有更优雅的方式来做到这一点?

4

6 回答 6

17

检查以下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).

于 2013-01-16T06:40:35.260 回答
9

这有效:

>>> l1,l2=['a', 'b', 'g', 'r'], ['e', 'g', 'l', 1, 'w']
>>> list(set(l1)&set(l2))
['g']
于 2013-01-16T06:42:44.150 回答
5

您不需要将两个lists 都转换为sets,只需一个。我认为跳过不必要的转换使其更具可读性和优雅性。

所以,要么:

set(a).intersection(b)

或者:

s = set(a)
any(e in s for e in b)

后者的优点是一旦找到一个匹配项就会短路,更好地表达逻辑,并返回TrueorFalse而不是 non-falsey 或 falsey set,但如果这让您感到困扰,它是两行而不是一行。我不知道这个优势是否抵消了将循环放在生成器表达式而不是 C 函数中的成本。

s 这么小的性能结果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

要将数字全部放在纳秒级,再加上set(a)除了最后需要的所有成本,并比较三个 Python 版本的相同测试(Apple 股票 CPython 2.7.2、python.org CPython 3.3.0、Homebrew PyPy 1.9.0/2.7.2,所有 64 位 Mac 版本):

                  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

这些更合理。他们仍然有意义。如果您要比较相同的 10000 个随机打乱的元素,那么每个元素要走多远?还不足以使set-ify 任何一个列表的成本都值得做,更不用说它们两个了!

所以,让我们尝试一个没有匹配的情况:

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

我不知道 PyPy 是如何如此快速地完成最后一个,但除此之外,这里没有任何惊喜。

那么,哪一个最好呢?

显然,如果您预计会有很多冲突,那么您希望尽可能避免制作集合——但如果您预计很少有冲突,您希望至少制作一组。如果你不知道,我认为最安全的赌注是any(genexp)——最坏的情况是最好的情况的不到 3 倍,如果有可能碰撞率很高,它会快很多。但是您可以查看数字并亲自查看。

或者,当然更好的是,根据您期望遇到的真实测试数据对它们进行计时。

于 2013-01-16T06:48:31.900 回答
2
def elm_exists(lista, listb):
    return bool(set(lista) & set(listb))

bool函数将返回True所有非空容器和空容器False。集合交集 ( &) 将返回两个集合的一组公共元素。请注意,集合将删除所有重复项。

或者,您可以set(lista).intersection(listb)bool函数中使用。

于 2013-01-16T06:43:44.477 回答
0
>>> y = [1,23,3]
>>> z = [3,432]
>>> (3 in y) and (3 in z)
True
于 2013-01-16T06:48:19.707 回答
0

这是另一种解决方案,

>>> c = [filter(lambda x: x in b, sublist) for sublist in a]
>>> filter (lambda a: a != '', c)
['g']
于 2013-01-16T06:49:05.093 回答