【问题标题】:Better/Faster to Loop through set or list?更好/更快地循环遍历集合或列表?
【发布时间】:2013-02-12 16:01:13
【问题描述】:

如果我有一个包含许多重复项的 python 列表,并且我想遍历每个项目,而不是重复项,最好使用一个集合(如set(mylist),或找到另一种创建方式没有重复的列表?我想只是循环遍历列表并检查重复项,但我认为这就是 set() 在初始化时所做的。

所以如果mylist = [3,1,5,2,4,4,1,4,2,5,1,3] 和我真的只想循环通过[1,2,3,4,5](顺序无关紧要),我应该使用set(mylist) 还是别的什么?

在最后一个示例中可以使用另一种方法,因为列表包含其最小值和最大值之间的每个整数,我可以循环遍历range(min(mylist),max(mylist))set(mylist)。在这种情况下,我通常应该尽量避免使用 set 吗?另外,查找minmax 会比创建set 慢吗?


在最后一个例子中,set 更快:

from numpy.random import random_integers
ids = random_integers(1e3,size=1e6)

def set_loop(mylist):
    idlist = []
    for id in set(mylist):
        idlist.append(id)
    return idlist

def list_loop(mylist):
    idlist = []
    for id in range(min(mylist),max(mylist)):
        idlist.append(id)
    return idlist

%timeit set_loop(ids)
#1 loops, best of 3: 232 ms per loop

%timeit list_loop(ids)
#1 loops, best of 3: 408 ms per loop

【问题讨论】:

  • 您是否认为这种速度差异在您编写的任何程序中真正重要?将内容保存在numpy 中,使用 genexp 而不是构建一百万个元素 list 只是为了迭代(如果这是 Py2,则使用 xrange 而不是 range),尝试在 C 中进行紧密循环Python(例如,idlist = range(…) 而不是做同样事情的for 循环)等都会使数量级的差异更大。
  • 更具体地说:set_loop 的整个主体相当于 return list(set(mylist))list_loop 在 2.x 中相当于 return range(min(mylist), max(mylist)),在 3.x 中相当于 return list(range(min(mylist), max(mylist)))。更简单的版本可能会或可能不会明显更快,但它们永远不会更慢,而且它们更容易阅读。
  • 您是否关心删除副本后列表是否保持相同的顺序?
  • @thewolf 顺序无关紧要,这就是我考虑设置的原因。
  • @askewchan:真的,你最好先写最易读的东西。如果您想要具有set 语义的东西,请使用set。如果程序结果很慢,并且分析显示构建或使用 set 是相关的,那么您可以寻找更快的解决方案。但是,如果您开始询问在程序中执行每个单独步骤的最快方法……那么,您应该用汇编而不是 Python 编写。

标签: python list loops set


【解决方案1】:

只需使用set。它的语义正是您想要的:独特项目的集合。

从技术上讲,您将遍历列表两次:一次用于创建集合,一次用于您的实际循环。但是使用任何其他方法,您会做同样多或更多的工作。

【讨论】:

    【解决方案2】:

    set 是你想要的,所以你应该使用set。试图变得聪明会引入一些微妙的错误,比如忘记在max(mylist) 中添加一个!防守代码。当您确定它太慢时,担心哪个更快。

    range(min(mylist), max(mylist) + 1)  # <-- don't forget to add 1
    

    【讨论】:

    • 我很想在这里您对我的回答的看法是我们处理大型列表时速度很快。
    【解决方案3】:

    虽然set 可能是您想要的结构,但问题是什么更快。列表更快。您的示例代码无法准确比较 setlist,因为您正在从列表转换为集合 in set_loop,然后您正在创建 list you'将循环通过 in list_loop。您迭代的集合和列表应该提前构造并在内存中,并简单地循环查看哪个数据结构在迭代时更快:

    ids_list = range(1000000)
    ids_set = set(ids)
    def f(x):
        for i in x:
             pass
    
    %timeit f(ids_set)
    #1 loops, best of 3: 214 ms per loop
    %timeit f(ids_list)
    #1 loops, best of 3: 176 ms per loop
    

    【讨论】:

    • 您的ids_list 不是一个实际列表,它是一个range 对象,但您的结果是正确的,set 确实需要比list 更长的时间。而range 甚至比set 还要慢。所以当&gt; 等于faster 然后list &gt; set &gt; range
    • 这取决于您使用的 python 版本。在 Python 2 中,range() 返回一个 list 对象。在 Python 3 中,它返回一个 range 生成器对象。不过,很高兴知道 python 3 的时间差异:range 对象有利于内存,但不利于速度。
    【解决方案4】:

    为简单起见:newList = list(set(oldList))

    但如果您想获得速度/排序/优化,还有更好的选择:http://www.peterbe.com/plog/uniqifiers-benchmark

    【讨论】:

    • 没有充分的理由返回列表。在将其转换为集合时,他已经丢失了元素顺序,因此没有理由不留在集合中。
    • @ThiefMaster 想回到一个列表是有原因的,主要是性能。列表的迭代速度比集合快得多,并且通过为每个元素保留一个内部属性,您可以轻松地将其转换回列表并将其排序为正确的顺序。
    【解决方案5】:

    我的列表变化很大,循环两次将花费大量时间,并且第二次循环一个集合而不是列表时会花费更多时间,而且我们知道迭代一个集合比列表慢。

    我认为你需要generatorset 的力量。

    def first_test():
    
        def loop_one_time(my_list):
            # create a set to keep the items.
            iterated_items = set()
            # as we know iterating over list is faster then list.
            for value in my_list: 
                # as we know checking if element exist in set is very fast not
                # metter the size of the set.
                if value not in iterated_items:  
                    iterated_items.add(value) # add this item to list
                    yield value
    
    
        mylist = [3,1,5,2,4,4,1,4,2,5,1,3]
    
        for v in loop_one_time(mylist):pass
    
    
    
    def second_test():
        mylist = [3,1,5,2,4,4,1,4,2,5,1,3]
        s = set(mylist)
        for v in s:pass
    
    
    import timeit
    
    print(timeit.timeit('first_test()', setup='from __main__ import first_test', number=10000))
    print(timeit.timeit('second_test()', setup='from __main__ import second_test', number=10000))
    

    输出:

       0.024003583388435043
       0.010424674188938422
    

    注意:此技术顺序是有保证的

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-22
      • 2016-10-08
      • 2018-06-16
      • 2016-07-16
      • 2014-08-13
      • 1970-01-01
      相关资源
      最近更新 更多