【问题标题】:Iterate a list as pair (current, next) in Python在Python中将列表迭代为对(当前,下一个)
【发布时间】:2011-07-23 00:52:32
【问题描述】:

有时我需要在 Python 中迭代一个列表,查看“当前”元素和“下一个”元素。到目前为止,我已经使用如下代码完成了此操作:

for current, next in zip(the_list, the_list[1:]):
    # Do something

这很有效,并且符合我的预期,但是有没有更惯用或更有效的方法来做同样的事情?

【问题讨论】:

  • 检查 this question 的 MizardX 答案。但我不认为这个解决方案比你的更惯用。
  • 因为没有其他人提到它,我就是那个人,并指出使用next这种方式掩盖了一个内置的。
  • imho zip(the_list, the_list[1:]) 在我看来确实是地道的。

标签: python


【解决方案1】:

从 Python 3.10 开始,这就是 pairwise 函数的确切作用:

from itertools import pairwise

list(pairwise([1, 2, 3, 4, 5]))
# [(1, 2), (2, 3), (3, 4), (4, 5)]

如果您不需要 list 形式的结果,也可以直接使用 pairwise([1, 2, 3, 4, 5])

【讨论】:

    【解决方案2】:

    正如其他人所指出的,itertools.pairwise() 是最新版本的 Python 的方法。但是,对于 3.8+,一个有趣且更简洁(与已发布的其他解决方案相比)的选项通过 walrus operator 提供,不需要额外导入:

    def pairwise(iterable):
      a = next(iterable)
      yield from ((a, a := b) for b in iterable)
    

    【讨论】:

      【解决方案3】:

      现在是 2020 年 5 月 16 日的简单导入

      from more_itertools import pairwise
      for current, next in pairwise(your_iterable):
        print(f'Current = {current}, next = {nxt}')
      

      Docs for more-itertools 在引擎盖下,此代码与其他答案中的代码相同,但我更喜欢可用的导入。

      如果您还没有安装它,那么: pip install more-itertools

      示例

      例如,如果你有斐波那契数列,你可以计算后续对的比率:

      from more_itertools import pairwise
      fib= [1,1,2,3,5,8,13]
      for current, nxt in pairwise(fib):
          ratio=current/nxt
          print(f'Curent = {current}, next = {nxt}, ratio = {ratio} ')
      

      【讨论】:

        【解决方案4】:

        我真的很惊讶没有人提到更短、更简单且最重要的通用解决方案:

        Python 3:

        from itertools import islice
        
        def n_wise(iterable, n):
            return zip(*(islice(iterable, i, None) for i in range(n)))
        

        Python 2:

        from itertools import izip, islice
        
        def n_wise(iterable, n):
            return izip(*(islice(iterable, i, None) for i in xrange(n)))
        

        它适用于通过传递n=2 进行成对迭代,但可以处理任何更高的数字:

        >>> for a, b in n_wise('Hello!', 2):
        >>>     print(a, b)
        H e
        e l
        l l
        l o
        o !
        
        >>> for a, b, c, d in n_wise('Hello World!', 4):
        >>>     print(a, b, c, d)
        H e l l
        e l l o
        l l o
        l o   W
        o   W o
          W o r
        W o r l
        o r l d
        r l d !
        

        【讨论】:

          【解决方案5】:

          这是来自itertools 模块文档的相关示例:

          import itertools
          def pairwise(iterable):
              "s -> (s0,s1), (s1,s2), (s2, s3), ..."
              a, b = itertools.tee(iterable)
              next(b, None)
              return zip(a, b)   
          

          对于 Python 2,您需要 itertools.izip 而不是 zip

          import itertools
          def pairwise(iterable):
              "s -> (s0,s1), (s1,s2), (s2, s3), ..."
              a, b = itertools.tee(iterable)
              next(b, None)
              return itertools.izip(a, b)
          

          这是如何工作的:

          首先,创建两个并行迭代器abtee() 调用),它们都指向原始可迭代对象的第一个元素。第二个迭代器 b 向前移动了 1 步(next(b, None))调用)。此时a指向s0,b指向s1。 ab 都可以独立地遍历原始迭代器 - izip 函数采用两个迭代器并将返回的元素配对,以相同的速度推进两个迭代器。

          一个警告:tee() 函数产生两个迭代器,它们可以相互独立地前进,但这是有代价的。如果其中一个迭代器比另一个更进一步,则tee() 需要将消耗的元素保留在内存中,直到第二个迭代器也使用它们(它不能“倒回”原始迭代器)。这里没关系,因为一个迭代器只领先另一个迭代器 1 步,但通常这种方式很容易使用大量内存。

          由于tee() 可以接受n 参数,这也可以用于两个以上的并行迭代器:

          def threes(iterator):
              "s -> (s0,s1,s2), (s1,s2,s3), (s2, s3,4), ..."
              a, b, c = itertools.tee(iterator, 3)
              next(b, None)
              next(c, None)
              next(c, None)
              return zip(a, b, c)
          

          【讨论】:

          • zip(ł, ł[1:]) 更短而且是 Python 的
          • @noɥʇʎԀʎzɐɹƆ:不,它不适用于每个可迭代对象,并且在列表中使用时会产生不必要的副本。使用函数是pythonic。
          • 这个函数在funcy模块中实现:funcy.pairwise:funcy.readthedocs.io/en/stable/seqs.html#pairwise
          【解决方案6】:

          自己动手吧!

          def pairwise(iterable):
              it = iter(iterable)
              a = next(it, None)
          
              for b in it:
                  yield (a, b)
                  a = b
          

          【讨论】:

          • 正是我需要的!这是否已被永生化为 python 方法,还是我们需要继续滚动?
          • @uhoh:据我所知还没有!
          • 我很惊讶这不是公认的答案。没有导入,其背后的逻辑很容易理解。绝对 +1。
          • 很快就会在 3.10 中以itertools.pairwise 的形式出现!
          【解决方案7】:

          我只是把它说出来,我很惊讶没有人想到 enumerate()。

          for (index, thing) in enumerate(the_list):
              if index < len(the_list):
                  current, next_ = thing, the_list[index + 1]
                  #do something
          

          【讨论】:

          • 其实if使用切片也可以去掉:for (index, thing) in enumerate(the_list[:-1]): current, next_ = thing, the_list[index + 1]
          • 这应该是真正的答案,它不依赖任何额外的导入并且效果很好。
          • 虽然,它不适用于不可索引的迭代,所以它不是一个通用的解决方案。
          【解决方案8】:

          按索引迭代可以做同样的事情:

          #!/usr/bin/python
          the_list = [1, 2, 3, 4]
          for i in xrange(len(the_list) - 1):
              current_item, next_item = the_list[i], the_list[i + 1]
              print(current_item, next_item)
          

          输出:

          (1, 2)
          (2, 3)
          (3, 4)
          

          【讨论】:

          • 您的答案更多的是 previouscurrent 而不是 currentnext,如在问题中。我做了一个改进语义的编辑,使i 始终是当前元素的索引。
          【解决方案9】:

          由于the_list[1:] 实际上创建了整个列表的副本(不包括其第一个元素),并且zip() 在调用时立即创建了一个元组列表,因此总共创建了三个列表副本。如果您的列表很大,您可能更喜欢

          from itertools import izip, islice
          for current_item, next_item in izip(the_list, islice(the_list, 1, None)):
              print(current_item, next_item)
          

          根本不复制列表。

          【讨论】:

          • 请注意,在 python 3.x 中,izip 被禁止使用 itertools,您应该使用内置 zip
          • 实际上,the_list[1:] 不只是创建一个切片对象而不是几乎整个列表的副本——所以 OP 的技术并不像你说的那么浪费。跨度>
          • 我认为[1:] 创建了切片对象(或者可能是“1:”),它被传递给列表中的__slice__,然后返回一个仅包含所选元素的副本。复制列表的一种惯用方式是l_copy = l[:](我觉得它丑陋且不可读——更喜欢l_copy = list(l)
          • @dcrosta:没有__slice__ 特殊方法。 the_list[1:] 等价于the_list[slice(1, None)],后者又等价于list.__getitem__(the_list, slice(1, None))
          • @martineau:the_list[1:] 创建的副本只是一个浅拷贝,因此每个列表项仅包含一个指针。更占用内存的部分是zip() 本身,因为它会为每个列表项创建一个包含一个tuple 实例的列表,每个列表项都包含两个指向这两个项的指针和一些附加信息。此列表将消耗由[1:] 引起的复制所消耗的内存量的九倍。
          【解决方案10】:

          使用列表推导从列表中配对

          the_list = [1, 2, 3, 4]
          pairs = [[the_list[i], the_list[i + 1]] for i in range(len(the_list) - 1)]
          for [current_item, next_item] in pairs:
              print(current_item, next_item)
          

          输出:

          (1, 2)
          (2, 3)
          (3, 4)
          

          【讨论】:

            【解决方案11】:
            code = '0016364ee0942aa7cc04a8189ef3'
            # Getting the current and next item
            print  [code[idx]+code[idx+1] for idx in range(len(code)-1)]
            # Getting the pair
            print  [code[idx*2]+code[idx*2+1] for idx in range(len(code)/2)]
            

            【讨论】:

              【解决方案12】:

              一个基本的解决方案:

              def neighbors( list ):
                i = 0
                while i + 1 < len( list ):
                  yield ( list[ i ], list[ i + 1 ] )
                  i += 1
              
              for ( x, y ) in neighbors( list ):
                print( x, y )
              

              【讨论】:

                猜你喜欢
                • 2015-11-05
                • 2022-12-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多