在python中减去列表,优化速度

时间:2019-05-21 22:41:52

标签: python list subtraction

为了找出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”

列表中没有重复任何元素,它们已经设置好了

3 个答案:

答案 0 :(得分:4)

只需将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对字符串进行相等性检查,而使用Sets时比较散列,后者便宜得多。

如果我正在正确阅读源代码,CPython's lists use a straight equality check to do comparisons when checking for membership似乎是正确的。大概,集使用散列,并且在创建集时将其缓存。

答案 1 :(得分:3)

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

diff = set(total_files) - set(files_downloaded)

如果您需要将输出作为列表:

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

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

正如您的问题所述,列表不包含重复项,并且行为类似于集合,这应该可以为您带来相对较好的性能。

答案 2 :(得分:1)

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