您不需要将两个list
都转换为set
,只需要转换其中一个。我认为跳过不必要的转换可以使代码更可读、更优美。
因此,可以选择以下任一方法:
set(a).intersection(b)
或者:
s = set(a)
any(e in s for e in b)
后者的优点在于发现一个匹配项后就立即短路,更好地表达逻辑,并返回
True
或
False
而不是非假或假的
set
,但如果这让您感到困扰,它将变成两行。我不知道这种优势是否抵消了将循环放在生成器表达式中而不是C函数内部的成本。
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
的成本不值得去做,更不用说两个列表都要转换了!
所以,让我们尝试一种没有匹配项的情况:
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倍,如果冲突率很高,它会快得多。但你可以看看数字并自行判断。
或者,更好的方法,当然是将它们与你预期遇到的真实测试数据进行比较。
Counter
代替set
来保留它。) - abarnert