假设我有两个表,l1和l2。我想执行l1 - l2,返回l1中不在l2中的所有元素。

我可以想出一个简单的循环方法来做这个,但那真的很低效。python式的高效方法是什么?

举个例子,如果l1 = [1,2,6,8], l2 = [2,3,5,8], l1 - l2应该返回[1,6]


当前回答

通过利用字典的有序属性来维持顺序(Python 3.7+)

注意:Python 3.6中字典的参考实现按照插入顺序维护键,但规范不保证这一点。对于3.7及更高版本,添加了这个保证。

字典的键作为一种集合;重复项被隐式过滤掉,由于散列,查找是高效的。因此,我们可以通过使用l1作为键来构建字典,然后删除出现在l2中的任何键来实现“set difference”。这维持了秩序并使用了一种快速的算法,但会产生相当数量的常量开销。

d = dict.fromkeys(l1)
for i in l2:
    try:
        del d[i]
    except KeyError:
        pass
l3 = list(d.keys())

其他回答

备选方案:

reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])

Python 3.8上的集合和列表理解基准

(加起来就是Moinuddin Quadri的基准)

tldr:使用Arkku的集合解决方案,相比之下比承诺的还要快!

根据列表检查现有文件

在我的例子中,我发现使用Arkku的集合解决方案要比python的列表理解方法快40倍(!)。

列表理解:

%%time
import glob
existing = [int(os.path.basename(x).split(".")[0]) for x in glob.glob("*.txt")]
wanted = list(range(1, 100000))
[i for i in wanted if i not in existing]

墙壁时间:28.2秒

Sets

%%time
import glob
existing = [int(os.path.basename(x).split(".")[0]) for x in glob.glob("*.txt")]
wanted = list(range(1, 100000))
set(wanted) - set(existing)

壁时间:689毫秒

一种方法是使用集合:

>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])

但是请注意,集合不会保留元素的顺序,并且会删除任何重复的元素。元素也需要是可哈希的。如果这些限制是可以容忍的,那么这通常是最简单和性能最高的选项。

试试这个:

l1=[1,2,6,8]
l2=[2,3,5,8]
r=[]
for x in l1:
    if x in l2:
        continue
    r=r+[x]
print(r)

使用Set推导式{x for x in l2}或Set (l2)来获取Set,然后使用List推导式来获取List

l2set = set(l2)
l3 = [x for x in l1 if x not in l2set]

基准测试代码:

import time

l1 = list(range(1000*10 * 3))
l2 = list(range(1000*10 * 2))

l2set = {x for x in l2}

tic = time.time()
l3 = [x for x in l1 if x not in l2set]
toc = time.time()
diffset = toc-tic
print(diffset)

tic = time.time()
l3 = [x for x in l1 if x not in l2]
toc = time.time()
difflist = toc-tic
print(difflist)

print("speedup %fx"%(difflist/diffset))

基准测试结果:

0.0015058517456054688
3.968189239501953
speedup 2635.179227x