【问题标题】:Keep two, skip two in Python list保留两个,在 Python 列表中跳过两个
【发布时间】:2022-01-07 20:42:56
【问题描述】:

我有一个 Python 列表

num_list = list(range(1,33))

并且需要列表中的每一对数字,如下所示:

[1, 2, 5, 6, 9, 10 ... ]

我已经想出了如何从列表中排除某些索引,就像这样

num_list[2::3]

> [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

但还没有弄清楚如何让它一次捕获两个索引。

【问题讨论】:

  • 也许:[i for i in range(1,33) if i%4 != 0 and i%4 != 3] 如果它总是从 1 到 N 的连续序列。

标签: python list indexing


【解决方案1】:

您可以使用enumerate,然后以数学方式过滤索引:

[v for i, v in enumerate(num_list) if i % 4 < 2]

【讨论】:

    【解决方案2】:

    你不能只用一个切片操作来做到这一点。你可以做类似的事情

    [num_list[j] for i in range(0, len(num_list), 4) for j in (i, i+1)]
    

    编辑:另一个选项是这样的:

    [x for i in range(0, len(num_list), 4) for x in num_list[i:i+2]]
    

    还有一个(懒惰的)选项是

    def keep_two_skip_two(it):
        it = iter(it)
        try:
            while True:
                yield next(it)
                yield next(it)
                next(it)
                next(it)
        except StopIteration:
            return
    
    list(keep_two_skip_two(num_list))
    

    【讨论】:

    • 为什么最后一个选项是惰性的?它似乎是不必要的冗长。
    • 它适用于任意迭代器,而不仅仅是列表。
    • “懒惰”,不像“懒惰程序员”,而是“懒惰(甚至无限)迭代器”
    • "Lazy" 将在每次迭代中执行,而不是一次性执行所有列表。
    【解决方案3】:

    您可以对列表进行两次切片,然后对其进行排序。

    sorted(num_list[0::4]+num_list[1::4])
    

    【讨论】:

      【解决方案4】:

      另一种解决方案是列表理解结合切片和压缩。切片仅提取所需的值, zips 将它们组合成对,在列表推导中,您可以遍历这些对以形成最终输出。在代码中,就像

      [p for pairs in zip(num_list[::4],num_list[1::4]) for p in pairs]
      

      【讨论】:

        【解决方案5】:

        你可以根据你的模式看起来更通用一些。您可以应用掩码,而不是使用基于索引的条件。例如

        mask = [1, 1, 0, 0]
        

        要创建重复值的掩码,使其与数据的长度相同,您可以zip 将数据与itertools.cycle(m) 放在一起。示例:

        import itertools
        
        
        def filter_mask(data, mask):
            return [d for d, m in zip(data, itertools.cycle(mask)) if m]
        

        或者按照@KellyBundy 的建议使用压缩!

        def filter_mask(data, mask):
            return list(itertools.compress(data, itertools.cycle(mask)))
        

        例子

        >>> data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        >>> filter_mask(data, mask=[1, 0])
        [0, 2, 4, 6, 8, 10]
        >>> filter_mask(data, mask=[0, 1])
        [1, 3, 5, 7, 9]
        >>> filter_mask(data, mask=[1, 1, 0, 0])
        [0, 1, 4, 5, 8, 9]
        >>> filter_mask(data, mask=[1, 0, 1, 1, 0])
        [0, 2, 3, 5, 7, 8, 10]
        

        【讨论】:

        • 啊,你打败了我cycle,但至少我没有重新发明compress :-P
        • @KellyBundy 啊,哈哈。我什至不知道那个!
        【解决方案6】:

        解决方案 1: 使用两个排除:

        del num_list[2::4]
        del num_list[2::3]
        

        现在你的清单是:

        [1, 2, 5, 6, 9, 10, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30]
        

        解决方案 2: 使用来自 itertoolscompresscycle

        list(compress(num_list, cycle([True, True, False, False]))))
        

        短版:

        [*compress(num_list, cycle([1, 1, 0, 0]))]
        

        【讨论】:

          【解决方案7】:

          为两个生成器使用不同的range 对象:

          >>> [x for i in range(1,33,4) for x in range(i, i+2)]
          [1, 2, 5, 6, 9, 10, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30]
          

          仅按需生成 2 元素范围的惰性版本:

          >>> from itertools import chain
          >>> x = chain.from_iterable(range(i, i+2) for i in range(1,33,4))
          >>> list(x)
          [1, 2, 5, 6, 9, 10, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30]
          

          【讨论】:

            【解决方案8】:

            这是一种在您的 sliced 列表中使用 itertools.chain.from_iterablezip 的实用方法:

            >>> num_list = list(range(1,33))
            >>> from itertools import chain
            
            >>> list(chain.from_iterable(zip(num_list[::4], num_list[1::4])))
            [1, 2, 5, 6, 9, 10, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30]
            

            【讨论】:

              猜你喜欢
              • 2021-06-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-12-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-08-11
              相关资源
              最近更新 更多