如何遍历列表中的重叠(当前,下一个)值对?
我有时候需要在Python中遍历一个列表,同时查看“当前”元素和“下一个”元素。到目前为止,我是用这样的代码来实现的:
for current, next in zip(the_list, the_list[1:]):
# Do something
这个方法有效,也达到了我想要的效果,但有没有更简单或者更高效的方法来做同样的事情呢?
一些针对这个问题的回答可以通过只处理每次两个元素的特定情况来简化。而对于每次处理N个元素的更一般情况,可以参考滚动或滑动窗口迭代器?。
13 个回答
49
从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])
。
58
自己动手做吧!
def pairwise(iterable):
it = iter(iterable)
a = next(it, None)
for b in it:
yield (a, b)
a = b
178
Python 3.8的文档提供了一个使用示例:
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
,这样可以得到一种懒惰的迭代器(而 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)
这个过程是怎么回事:
首先,创建了两个并行的迭代器,分别叫 a
和 b
(通过 tee()
函数),它们都指向原始可迭代对象的第一个元素。然后,第二个迭代器 b
向前移动一步(通过 next(b, None)
)。此时,a
指向 s0,而 b
指向 s1。接下来,a
和 b
可以独立地遍历原始的迭代器 - izip
函数会把这两个迭代器的返回元素配对,并且两个迭代器会以相同的速度前进。
因为 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)
注意:如果通过 tee
生成的某个迭代器走得比其他的更远,那么实现方式就需要把已经消费的元素保存在内存中,直到每个迭代器都消费完这些元素(它不能“倒回”原始迭代器)。在这里没问题,因为一个迭代器只比另一个快一步,但一般来说,这样使用可能会占用很多内存。