如何迭代来自列表的重叠(当前,下一个)值对?

有时候,我需要在 Python 中迭代一个列表,查看“ current”元素和“ next”元素。到目前为止,我已经用以下代码完成了这些工作:

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

这种方法起作用并且达到了我所期望的效果,但是有没有一种更惯用或更有效的方法来做同样的事情呢?


这个问题的一些答案可以通过解决一次只取两个元素的 具体点情况来简化。对于一次 N 个元素的 将军情况,请参见 滚动或滑动窗口迭代器?

70054 次浏览

由于 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)

它根本不会复制列表。

下面是来自 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)

工作原理:

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

需要注意的是: tee()函数产生两个迭代器,它们可以相互独立地前进,但这是有代价的。如果一个迭代器比另一个迭代器进展得更快,那么 tee()需要将消耗的元素保留在内存中,直到第二个迭代器也消耗这些元素(它不能对原始迭代器进行“倒带”)。这里没有关系,因为一个迭代器只比另一个迭代器快一步,但通常这样很容易使用大量内存。

由于 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)

通过索引进行迭代可以做同样的事情:

#!/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)

一个基本的解决方案:

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 )
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)]

使用列表内涵从列表中配对

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)

滚你自己的!

def pairwise(iterable):
it = iter(iterable)
a = next(it, None)


for b in it:
yield (a, b)
a = b

我只是把这个放出去,我很惊讶没有人想到枚举()。

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

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

巨蟒3:

from itertools import islice


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

巨蟒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 !

这是2020年5月16日的简单进口

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

更多迭代工具的文档 实际上,这些代码与其他答案中的代码是相同的,但是我更喜欢在可用时导入。

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

例子

例如,如果你有 fibbonnacci 序列,你可以计算后续对的比例如下:

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} ')

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

from itertools import pairwise


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

或者简单的 pairwise([1, 2, 3, 4, 5]),如果你不需要结果作为一个 list

正如其他人指出的那样,itertools.pairwise()是最新版本的 Python 的发展方向。然而,对于3.8 + 来说,通过 海象操作员提供了一个有趣且更简洁(与已发布的其他解决方案相比)的不需要额外导入的选项:

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