检查两个给定列表中是否存在元素的最简单,最优雅的方法是什么?例如,我有两个列表如下?
>>>a, b = ['a', 'b', 'g', 'r'], ['e', 'g', 'l', 1, 'w']
Run Code Online (Sandbox Code Playgroud)
现在在上面给出的列表中,我想检查两个列表中是否存在任何元素.目前我正在做如下
def elm_exists(list_a, list_b):
for elm in list_a:
if elm in list_b:
return True
return False
Run Code Online (Sandbox Code Playgroud)
有更优雅的方式吗?
nam*_*mit 13
检查a以及的元素b:
set(a).intersection(b)
Run Code Online (Sandbox Code Playgroud)
例:
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
Run Code Online (Sandbox Code Playgroud)
也:
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
Run Code Online (Sandbox Code Playgroud)
因此使用set(a).intersection(b).
这有效:
>>> l1,l2=['a', 'b', 'g', 'r'], ['e', 'g', 'l', 1, 'w']
>>> list(set(l1)&set(l2))
['g']
Run Code Online (Sandbox Code Playgroud)
您不需要将两个lists 都转换为sets,只需一个即可。我认为跳过不必要的转换会使它更具可读性和优雅性。
所以,要么:
\n\nset(a).intersection(b)\nRun Code Online (Sandbox Code Playgroud)\n\n或者:
\n\ns = set(a)\nany(e in s for e in b)\nRun Code Online (Sandbox Code Playgroud)\n\n后者的优点是一旦找到一个匹配就短路,并且可以更好地表达逻辑,并返回TrueorFalse而不是非 falsey 或 falsey set,但它是两行而不是一行,如果这让您烦恼的话。我不知道这个优点是否抵消了将循环放入生成器表达式而不是 C 函数中的成本。
s 这么小的性能结果list几乎没有意义,所以让我们试试这个:
In [373]: a=[random.choice(string.ascii_lowercase) for _ in range(10000)]\nIn [374]: b=[random.choice(string.ascii_lowercase) for _ in range(10000)]\nIn [375]: %timeit(set(a))\n10000 loops, best of 3: 180 us per loop\nIn [376]: s=set(a) # since all versions need to do this\nIn [391]: %timeit(s & set(b))\n1000000 loops, best of 3: 178 us per loop\nIn [392]: %timeit(s.intersection(b))\n1000000 loops, best of 3: 247 us per loop\nIn [393]: %timeit(discard(e in s for e in b))\n1000000 loops, best of 3: 550 ns per loop\nIn [394]: %timeit(any(e in s for e in b))\n1000000 loops, best of 3: 749 ns per loop\nIn [395]: %timeit(any(e in a for e in b))\n1000000 loops, best of 3: 1.42 us per loop\nRun Code Online (Sandbox Code Playgroud)\n\n要将所有数字都放在纳秒级别,请添加除set(a)最后一个需求之外的所有成本,并比较三个 Python 版本(Apple stock CPython 2.7.2、python.org CPython 3.3.0、Homebrew)的相同测试PyPy 1.9.0/2.7.2,所有 64 位 Mac 版本):
CP272 CP330 PyPy\ns & set(b) 358000 316000 180500\ns.intersection(b) 427000 459000 180900\ndiscard(genexp) 180550 157341 90094\nany(genexp) 180749 157334 90208\nany(list-genexp) 1420 686 960\nRun Code Online (Sandbox Code Playgroud)\n\n现在我想起来,这完全有道理。很早就发生碰撞的可能性非常高,因此将整个事物转换为集合的成本决定了一切。
\n\n这意味着我们需要一个包含 10000 个唯一值的新测试。让我们重复一下测试:
\n\nIn [29]: a, b = list(range(10000)), list(range(10000))\nIn [30]: random.shuffle(a)\nIn [31]: random.shuffle(b)\n\n CP272 CP330 PyPy\ns & set(b) 1277000 1168000 1141000\ns.intersection(b) 1165000 1117000 2520000\ndiscard(genexp) 1699000 1271000 770000\nany(genexp) 389800 344543 320807\nany(list-genexp) 62000 10400 1520\nRun Code Online (Sandbox Code Playgroud)\n\n这些都比较合理。它们仍然有道理。如果您要比较随机洗牌的 10000 个相同元素,您需要对每个元素进行多深的比较?还不足以使set任何一个列表的验证成本值得做,更不用说两个列表了!
那么,让我们尝试一下没有匹配项的情况:
\n\nIn [43]: a=list(range(10000, 20000))\n\n\n CP272 CP330 PyPy\ns & set(b) 751000 770000 733000\ns.intersection(b) 466000 530000 1920000\ndiscard(genexp) 1246000 985000 749000\nany(genexp) 1269000 966000 893000\nany(list-genexp) 185000000 176000000 5870000\nRun Code Online (Sandbox Code Playgroud)\n\n我不知道 PyPy 如何如此快地完成最后一个,但除此之外,这里没有什么惊喜。
\n\n那么,哪一个最好呢?
\n\n显然,如果您期望发生大量碰撞,则希望尽可能避免创建集合\xe2\x80\x94,但如果您期望发生很少的碰撞,则希望至少创建一组。如果你不知道,我认为最安全的选择是any(genexp)\xe2\x80\x94 在最坏的情况下它比最好的差不到 3 倍,如果有可能碰撞率很高,它会快很多。但你可以看看这些数字并亲自看看。
或者,当然更好的是,根据您期望遇到的真实测试数据对它们进行计时。
\n