【问题标题】:Pythonic way to return list of every nth item in a larger listPythonic方法返回更大列表中每个第n个项目的列表
【发布时间】:2010-11-27 01:59:05
【问题描述】:

假设我们有一个从 0 到 1000 的数字列表。是否有一种 pythonic/有效的方法来生成第一个和每个后续第 10 个项目的列表,即[0, 10, 20, 30, ... ]

是的,我可以使用 for 循环来做到这一点,但我想知道是否有更简洁的方法来做到这一点,甚至可能在一行中?

【问题讨论】:

    标签: list python


    【解决方案1】:
    >>> lst = list(range(165))
    >>> lst[0::10]
    [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
    

    请注意,这比循环和检查每个元素的模数快大约 100 倍:

    $ python -m timeit -s "lst = list(range(1000))" "lst1 = [x for x in lst if x % 10 == 0]"
    1000 loops, best of 3: 525 usec per loop
    $ python -m timeit -s "lst = list(range(1000))" "lst1 = lst[0::10]"
    100000 loops, best of 3: 4.02 usec per loop
    

    【讨论】:

    • 当然,列表推导通常更强大。 OTOH,问题提出了一个现有列表,在这种情况下,切片就可以了。
    • 我在下面的列表理解答案中对此进行了评论。小心“如果 x % 10 == 0”。它仅适用于这个特定的列表示例,但如果输入列表是例如 l=range(0,1000,2) 它不会每 10 个项目拉出一次。
    • @Andre:非常正确。所以这是一个不起眼的语言特性的例子,切片运算符,在这种情况下 (1) 可以更容易地获得正确的结果; (2) 表达更简洁; (3) 恰好快 2 个数量级。 (1) 是迄今为止最重要的问题,当然,但是,由于语言的精心设计和实现,您可以以 1 的价格获得所有三个。很好的问题和响应。
    • 0l[0::10] 中是多余的。 l[::10] 更具可读性,更少混乱。
    • 您的比较不合适,因为您选择的是每一个可被 10 整除的元素,而不是选择每 10 个元素。将切片符号与此进行比较会更合适:lst = list(range(1000)); lst1 = [lst[i] for i in range(0, len(lst), 10)]。在我的机器上,我得到“1000000 循环,最好的 5:每循环 395 纳秒”用于切片符号和“20000 循环,最好的 5:每循环 11.1 微秒”用于列表理解。
    【解决方案2】:
    1. source_list[::10] 是最明显的,但这不适用于任何可迭代对象,并且对于大型列表而言内存效率不高。
    2. itertools.islice(source_sequence, 0, None, 10) 适用于任何可迭代对象且内存效率高,但可能不是大型列表和大步的最快解决方案。
    3. (source_list[i] for i in xrange(0, len(source_list), 10))

    【讨论】:

    • +1 最佳答案,IMO。所有三个提案都是一个通用解决方案(即,将源列表作为给定)。生成器解决方案 (3.) 很好,因为它过滤源列表的索引。它的内存效率可能与 2 一样。索引和结果列表都是生成器,因此是惰性构建的,如果您不需要单个块中的结果列表,这也可能是最快的。只有当源列表可以是生成器时,我才会使用 Paul 的“item, i in enumerate(l)”习语,因为没有生成器的 len()。顺便说一句,哪种迭代不适用于 1.?发电机?!
    • Iterable = 带有 __iter__() 方法的对象返回迭代器(带有 next() 方法的对象)
    【解决方案3】:

    你可以像这样使用切片操作符:

    l = [1,2,3,4,5]
    l2 = l[::2] # get subsequent 2nd item
    

    【讨论】:

    • 如何获取从第 3 项开始的每 2 项?
    • @user1993 L[2::2]
    【解决方案4】:

    使用range(start, end, step)

    li = list(range(0, 1000, 10))
    
    [0, 10, 20, 30, 40, 50, 60, 70, 80, 90 ... 990]
    

    或者,如果您有一个列表,请使用 slice:来自手册:s[i:j:k] slice of s from i to j with step k

    yourlist = [0, ... ,10 ...]  
    sub = yourlist[::10]  # same as yourlist[0:100:10]
    
    >>> sub
    [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
    

    【讨论】:

      【解决方案5】:
      newlist = oldlist[::10]
      

      这会挑选列表中的每 10 个元素。

      【讨论】:

        【解决方案6】:

        为什么不直接使用 range 函数的 step 参数来获取:

        l = range(0, 1000, 10)
        

        为了比较,在我的机器上:

        H:\>python -m timeit -s "l = range(1000)" "l1 = [x for x in l if x % 10 == 0]"
        10000 loops, best of 3: 90.8 usec per loop
        H:\>python -m timeit -s "l = range(1000)" "l1 = l[0::10]"
        1000000 loops, best of 3: 0.861 usec per loop
        H:\>python -m timeit -s "l = range(0, 1000, 10)"
        100000000 loops, best of 3: 0.0172 usec per loop
        

        【讨论】:

        • @SilentGhost:确实如此,但由于这是一个初学者问题,因此范围函数可能是他们真正想要做的,所以我认为这是一个有效的答案。 (虽然上限应该是1001,而不是1000)
        【解决方案7】:
        existing_list = range(0, 1001)
        filtered_list = [i for i in existing_list if i % 10 == 0]
        

        【讨论】:

        • 当 range(0, 1001, 10) 已经只取每 10 个元素时,为什么还有 if 子句?
        • 这里有同样的评论,这并不能解决更普遍的问题“Pythonic方式返回更大列表中每个第n个项目的列表”您的解决方案取决于示例列表的值是 0 到 1000,并且只从列表中拉出值可被 10 整除的项目,而不是每 10 个项目。
        • 好吧,OP 写道:“我们有一个从零到 1000 的数字列表”。所以他不需要通用的解决方案。
        • 他写道“说我们有..”,这意味着它只是一个例子。如果他真的想要从 0 到 1000 的列表中的每 10 个数字,那么答案将是 range(0,1001,10) 或类似的东西。
        【解决方案8】:

        这是“每 10 个项目”列表理解的更好实现,它不使用列表内容作为成员资格测试的一部分:

        >>> l = range(165)
        >>> [ item for i,item in enumerate(l) if i%10==0 ]
        [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
        >>> l = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
        >>> [ item for i,item in enumerate(l) if i%10==0 ]
        ['A', 'K', 'U']
        

        但这仍然比仅使用列表切片要慢得多。

        【讨论】:

          【解决方案9】:

          列表推导正是为此而生的:

          smaller_list = [x for x in range(100001) if x % 10 == 0]
          

          您可以在 python 官方文档中获得有关它们的更多信息: http://docs.python.org/tutorial/datastructures.html#list-comprehensions

          【讨论】:

          • 上限应该是 1000,而不是 10000。您的解决方案不包括上限 1000,因为范围停止在 999。+1 表示指向列表理解的链接。
          • 这实际上并不是每 10 个项目都提取,它会提取所有值可被 10 整除的项目。在这个特定示例中,它是相同的,但可能不是。
          猜你喜欢
          • 1970-01-01
          • 2015-09-22
          • 1970-01-01
          • 1970-01-01
          • 2021-12-25
          • 1970-01-01
          • 2012-01-10
          • 1970-01-01
          相关资源
          最近更新 更多