【问题标题】:Iterate over all pairs of consecutive items in a list [duplicate]迭代列表中的所有连续项目对[重复]
【发布时间】:2014-02-13 17:00:50
【问题描述】:

给定一个列表

l = [1, 7, 3, 5]

我想遍历所有连续列表项对(1,7), (7,3), (3,5),即

for i in xrange(len(l) - 1):
    x = l[i]
    y = l[i + 1]
    # do something

我想以更紧凑的方式来做这件事,比如

for x, y in someiterator(l): ...

有没有办法使用内置的 Python 迭代器来做到这一点?我确定itertools 模块应该有解决方案,但我就是想不通。

【问题讨论】:

  • 虽然我接受了 sberry 的回答,因为我要求提供一个简单的基于内置的解决方案,但也要考虑 thefourtheye 和 HansZauber 提供的优雅且性能更高的解决方案。

标签: python list iterator


【解决方案1】:

只需使用zip

>>> l = [1, 7, 3, 5]
>>> for first, second in zip(l, l[1:]):
...     print first, second
...
1 7
7 3
3 5

如果您使用 Python 2(不建议),您可能会考虑使用 itertools 中的 izip 函数来处理您不想创建新列表的非常长的列表。

import itertools

for first, second in itertools.izip(l, l[1:]):
    ...

【讨论】:

  • Meh... 在 Python 2 中,zip() 返回一个新列表。最好使用itertools.izip()
  • 在 Python 3 中,zip() 返回一个迭代器。更好地使用 Python 3。
  • 谢谢,但不应该是zip(l[:-1], l[1:]) 而不是zip(l, l[1:])
  • 这会无缘无故地创建l(几乎所有元素)的副本。
  • @flonk,不一定,因为 zip 会尝试创建完整的组。
【解决方案2】:

在 itertools 食谱中查看 pairwisehttp://docs.python.org/2/library/itertools.html#recipes

从那里引用:

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

一般版本

生成任何给定正自然大小的元组的通用版本可能如下所示:

def nwise(iterable, n=2):                                                      
    iters = tee(iterable, n)                                                     
    for i, it in enumerate(iters):                                               
        next(islice(it, i, i), None)                                               
    return izip(*iters)   

【讨论】:

  • 我喜欢这种方法,因为它不会复制可迭代的输入。对于python3,只需使用zip 而不是izip
  • 如何将其扩展为还包括 (sLast, s0)?所以不是产生 n-1 对,而是返回 n 对?
  • @normanius 我认为扩展它的最简单方法是在iterable 的结尾处填充相关值的副本:nwise(chain(a, islice(b, n-1)), n) where a, b = tee(iterable)
【解决方案3】:

我会创建一个通用的grouper 生成器,像这样

def grouper(input_list, n = 2):
    for i in xrange(len(input_list) - (n - 1)):
        yield input_list[i:i+n]

样品运行 1

for first, second in grouper([1, 7, 3, 5, 6, 8], 2):
    print first, second

输出

1 7
7 3
3 5
5 6
6 8

样品运行 1

for first, second, third in grouper([1, 7, 3, 5, 6, 8], 3):
    print first, second, third

输出

1 7 3
7 3 5
3 5 6
5 6 8

【讨论】:

  • 你可以这样写生成器理解pair_generator = ((list[i], list[i+1]) for i in range(0, len(list)-1))
【解决方案4】:

用理解概括 sberry 的 nwise 方法:

def nwise(lst, k=2):
    return list(zip(*[lst[i:] for i in range(k)])) 

例如

nwise(list(range(10)),3)

[(0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7), (6, 7, 8), (7, 8, 9)]

【讨论】:

    【解决方案5】:

    无需不必要的复制即可做到这一点的一种简单方法是存储前一个元素的生成器。

    def pairs(iterable):
        """Yield elements pairwise from iterable as (i0, i1), (i1, i2), ..."""
        it = iter(iterable)
        try:
            prev = next(it)
        except StopIteration:
            return
        for item in it:
            yield prev, item
            prev = item
    

    与基于索引的解决方案不同,这适用于任何可迭代对象,包括那些不支持索引(例如生成器)或速度较慢(例如collections.deque)的迭代。

    【讨论】:

      【解决方案6】:

      您可以使用zip

      >>> list(zip(range(5), range(2, 6)))
      [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
      

      就像拉链一样,它会成对。所以,要混合你的两个列表,你会得到:

      >>> l = [1,7,3,5]
      >>> list(zip(l[:-1], l[1:]))
      [(1, 7), (7, 3), (3, 5)]
      

      然后迭代就像

      for x, y in zip(l[:-1], l[1:]):
          pass
      

      【讨论】:

      • 你不需要修剪第一个的结尾,因为 zip 只会组成完整的组。如果您使用izip_longest,情况会有所不同,但是您为什么要这样做。
      • @sberry:你是对的,但我更喜欢这样明确。我猜这是个人的事情。
      【解决方案7】:

      如果你想要一些内联但可读性不强的东西,这里是另一种使用生成器的解决方案。我希望这也不是最好的表现:-/

      将列表转换为生成器,并在最后一项之前结束:

      gen = (x for x in l[:-1])
      

      将其转换成对:

      [(gen.next(), x) for x in l[1:]]
      

      这就是你所需要的。

      【讨论】:

      • 对于l = [1, 2, 3, 4],这会产生[(1, 2), (3, 4)] 而不是[(1, 2), (2, 3), (3, 4)] 所要求的。它也仅在列表包含偶数个项目时才有效。
      • 哎呀,你是对的。对不起,我不应该在没有测试的情况下在互联网上发布废话。如果您对这种形式的解决方案感兴趣,我已经更正了它现在可以工作(希望如此)。
      猜你喜欢
      • 1970-01-01
      • 2018-11-03
      • 1970-01-01
      • 2011-02-01
      • 2020-03-17
      • 1970-01-01
      • 2019-10-25
      • 2020-10-29
      相关资源
      最近更新 更多