如何筛选出两个巨大列表的列表项?

时间:2017-08-11 08:12:40

标签: python performance

我有一个巨大的列表,它是all_entries(目前是80k整数项)。在此列表中包含我已在整个程序中处理的项目。

当我的程序使用以下方法时,通常需要大约30秒才能到达return语句。我怎样才能加快速度呢?

提示:new_entries长40公斤,也是如此之大。

def get_fresh_entries(self, new_entries, all_entries):
    """
    :param new_entries: Entries from which some might already be in all_entries.
    :param all_entries: Entries already handled and saved.
    """
    fresh = []
    shuffle(new_entries)

    for i in new_entries:
        if i not in all_entries:
            fresh.append(i)
        if len(fresh) > 80000:
            break
    return fresh

2 个答案:

答案 0 :(得分:3)

唯一问题是为每个新条目执行的行if i not in all_entries:,并针对最多80,000个现有条目进行测试。

重要的是要理解在列表或集合上执行测试时的区别。

  • 测试一个元素是否在列表中就像测试某人是否在家而不知道地址(只是城镇)并挨家挨户地去。
  • 测试一个元素是否在一个集合中就像测试某人是否在家,知道确切地址并敲响一个门铃

因此,只需将all_entries转换为一次设置(!)即可消除主要速度问题。

...
all_entries_set = set(all_entries)
for i in new_entries:
    if i not in all_entries_set:
        ...

虽然还有其他提示如何使用集合加速程序是至关重要的,因为它会降低复杂性。

答案 1 :(得分:2)

列表理解将会:

作为 @Delgan 评论,如果all_entries是一个集合,则更好。

all_entries = set(all_entries)

然后:

fresh = [x for x in new_entries if x not in all_entries]

另请查看itertools.ifilter,它是lazyly evaluated

fresh = itertools.ifilter(lambda x: x not in all_entries, new_entries)

如果你只需要保留第一个n数据,因为itertools很懒,你可以像这样:

fresh = itertools.islice(itertools.ifilter(lambda x: x not in all_entries, 
                                           new_entries),
                         n))

或与列表理解相似,但改为使用生成器:

fresh = itertools.islice((x for x in new_entries if x not in all_entries), n)