python 列表减法,优化速度

subtraction of lists in python, optimising speed

为了找出 python 中两个列表的替换,我使用:

names_of_files_not_dowloaded = [item for item in total_files if item not in names_of_files_downloaded]

有效。

列表的大小是:

文件总数56373个元素

已下载文件列表 28464 个元素

持续34秒。 不知何故,我的直觉是 34 秒太长了。 有什么方法可以更有效地进行减法吗?

谢谢

编辑: 元素类似于 'AB12345'

这些列表没有任何重复的元素,它们已经是集合了

只需将 files_downloaded 设为集合而不是列表。列表可能需要列表的完整迭代来进行成员资格检查,每次您想进行检查。但是集合是 much more efficient to do a lookup on.

只需使用:

downloaded_set = set(files_downloaded)
list_of_files_not_dowloaded = [item for item in total_files if item not in downloaded_set]

将列表放入集合中需要初始成本,但之后的成员资格检查会快得多。


@juanpa.arrivillaga 在评论中还提到,造成性能下降的另一个原因是 in 对字符串进行相等性检查,而使用 Set 时会比较哈希,而后者要多得多便宜。

看起来,如果我没看错来源,CPython's lists use a straight equality check to do comparisons when checking for membership。据推测,Sets 使用哈希,并且它们在 Set 创建时被缓存。

total_files_set = set(total_files)
files_downloaded_set = set(files_downloaded)
files_not_dowloaded_set = total_files_set - files_downloaded_set 
list_of_files_not_dowloaded = list(files_not_dowloaded_set)

或者如果你想在一行中:

list_of_files_not_dowloaded = list(set(total_files) - set(files_downloaded))

想了解更多关于集合的所有操作,可以查看here

编辑:
我尝试使用 2 个随机列表对这两种方法进行计时

  • 对于具有 50,000 个元素的子集和具有 100,000 个元素的超集
timeit.timeit('l = list(set(l1)-set(l2))', 
setup='import random; l1 = random.sample(range(1000000), 100000); l2 = random.sample(range(1000000), 50000)', 
number = 10)

输出:

0.39393879500130424

timeit.timeit('l = [item for item in l1 if item not in l2]', \
setup='import random; l1 = random.sample(range(1000000), 10000); l2 = random.sample(range(1000000), 5000)', \
number = 1)

输出:

98.58012624000003

如果您恰好已经拥有这两个集合,而不必从列表中转换:

timeit.timeit('l = list(s2-s1)', 
setup='import random; s1 = set(random.sample(range(1000000), 100000)); s2 = set(random.sample(range(1000000), 50000))', 
number = 10)

输出:

0.06160322100004123

如果您不关心元素的顺序并且您的列表不包含重复项,您可以简单地使用:

diff = set(total_files) - set(files_downloaded)

如果您需要以列表形式输出:

diff = list(set(total_files) - set(files_downloaded))

set 覆盖了 __sub__() 方法并将其用作设置差异,这就是您要查找的内容。

正如你的问题所说,列表不包含重复项并且表现得像集合,这应该会以相对较好的性能满足你的需求。