遍历列表中的每两个元素

我如何使一个for循环或一个列表理解,以便每次迭代给我两个元素?

l = [1,2,3,4,5,6]


for i,k in ???:
print str(i), '+', str(k), '=', str(i+k)

输出:

1+2=3
3+4=7
5+6=11
320963 次浏览

你需要一个pairwise()(或grouped())实现。

def pairwise(iterable):
"s -> (s0, s1), (s2, s3), (s4, s5), ..."
a = iter(iterable)
return zip(a, a)


for x, y in pairwise(l):
print("%d + %d = %d" % (x, y, x + y))

或者,更一般地说:

def grouped(iterable, n):
"s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
return zip(*[iter(iterable)]*n)


for x, y in grouped(l, 2):
print("%d + %d = %d" % (x, y, x + y))

在Python 2中,你应该导入izip来替换Python 3的内置zip()函数。

他的回答我的问题的所有信用都归于马提瑙,我发现这是非常有效的,因为它只在列表上迭代一次,并且不会在过程中创建任何不必要的列表。

注意:这不应该与Python自己的< >强itertools < /强>文档中的< >强pairwise < / >强配方混淆,后者产生s -> (s0, s1), (s1, s2), (s2, s3), ...,正如@lazyr在评论中指出的那样。

对于那些想在Python 3上使用mypy进行类型检查的人来说,这是一个小小的补充:

from typing import Iterable, Tuple, TypeVar


T = TypeVar("T")


def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
"""s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
return zip(*[iter(iterable)] * n)

你需要2个元素的元组

data = [1,2,3,4,5,6]
for i,k in zip(data[0::2], data[1::2]):
print str(i), '+', str(k), '=', str(i+k)

地点:

  • data[0::2]表示创建元素的子集集合
  • zip(x,y)从x和y集合中创建一个相同索引元素的元组集合。

一个简单的解决方案。

l = [1, 2, 3, 4, 5, 6]


for i in range(0, len(l), 2):
print str(l[i]), '+', str(l[i + 1]), '=', str(l[i] + l[i + 1])
>>> l = [1,2,3,4,5,6]


>>> zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]


>>> zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]


>>> [a+b for a,b in zip(l,l[1:])[::2]]
[3, 7, 11]


>>> ["%d + %d = %d" % (a,b,a+b) for a,b in zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']
for (i, k) in zip(l[::2], l[1::2]):
print i, "+", k, "=", i+k

zip(*iterable)返回一个元组,其中包含每个可迭代对象的下一个元素。

l[::2]返回列表的第1、3、5等元素:第一个冒号表示切片从开头开始,因为后面没有数字,第二个冒号只在你想要“切片中的步骤”(在本例中为2)时才需要。

l[1::2]做同样的事情,但从列表的第二个元素开始,因此它返回原始列表的第2、4、6等元素。

虽然使用zip的所有答案都是正确的,但我发现自己实现功能会导致更可读的代码:

def pairwise(it):
it = iter(it)
while True:
try:
yield next(it), next(it)
except StopIteration:
# no more elements in the iterator
return

it = iter(it)部分确保it实际上是一个迭代器,而不仅仅是一个可迭代器。如果it已经是一个迭代器,这一行是一个无操作。

用法:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
print(a + b)

我认为这是一个分享我对n>2的概括的好地方,它只是一个可迭代对象上的滑动窗口:

def sliding_window(iterable, n):
its = [ itertools.islice(iter, i, None)
for i, iter
in enumerate(itertools.tee(iterable, n)) ]


return itertools.izip(*its)

我希望这是一种更优雅的方法。

a = [1,2,3,4,5,6]
zip(a[::2], a[1::2])


[(1, 2), (3, 4), (5, 6)]

这里我们可以有alt_elem方法,它可以适合你的for循环。

def alt_elem(list, index=2):
for i, elem in enumerate(list, start=1):
if not i % index:
yield tuple(list[i-index:i])




a = range(10)
for index in [2, 3, 4]:
print("With index: {0}".format(index))
for i in alt_elem(a, index):
print(i)

输出:

With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)

注意:考虑到在func中执行的操作,上述解决方案可能不是有效的。

对于可能有所帮助的人,这里有一个类似问题的解决方案,但有重叠的对(而不是互斥的对)。

来自Python itertools文档:

from itertools import izip


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

或者,更一般地说:

from itertools import izip


def groupwise(iterable, n=2):
"s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
t = tee(iterable, n)
for i in range(1, n):
for j in range(0, i):
next(t[i], None)
return izip(*t)

这个问题的标题有误导性,你似乎在寻找连续的对,但如果你想遍历所有可能的对的集合,那么这样做是可行的:

for i,v in enumerate(items[:-1]):
for u in items[i+1:]:

你可以使用more_itertools包。

import more_itertools


lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
print(f'{i} + {j} = {i+j}')

zipiter命令一起使用:

我发现这个使用iter的解决方案非常优雅:

it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]

我在Python 3 zip文档中找到的。

it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='\n')


# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11

一次泛化到N元素:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]

如果你对性能感兴趣,我做了一个小的基准测试(使用我的库simple_benchmark)来比较解决方案的性能,我包括了一个包中的函数:iteration_utilities.grouper

from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder


bench = BenchmarkBuilder()


@bench.add_function()
def Johnsyweb(l):
def pairwise(iterable):
"s -> (s0, s1), (s2, s3), (s4, s5), ..."
a = iter(iterable)
return zip(a, a)


for x, y in pairwise(l):
pass


@bench.add_function()
def Margus(data):
for i, k in zip(data[0::2], data[1::2]):
pass


@bench.add_function()
def pyanon(l):
list(zip(l,l[1:]))[::2]


@bench.add_function()
def taskinoor(l):
for i in range(0, len(l), 2):
l[i], l[i+1]


@bench.add_function()
def mic_e(it):
def pairwise(it):
it = iter(it)
while True:
try:
yield next(it), next(it)
except StopIteration:
return


for a, b in pairwise(it):
pass


@bench.add_function()
def MSeifert(it):
for item1, item2 in grouper(it, 2):
pass


bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)

enter image description here

所以,如果你想要一个没有外部依赖的最快的解决方案,你可能应该使用Johnysweb给出的方法(在撰写本文时,它是得到最多好评和接受的答案)。

如果你不介意额外的依赖,那么iteration_utilities中的grouper可能会更快一些。

额外的想法

有些方法有一些限制,这里没有讨论。

例如,一些解决方案只适用于序列(即列表,字符串等),例如Margus/pyanon/taskinoor解决方案使用索引,而其他解决方案适用于任何可迭代对象(即序列而且生成器,迭代器),如Johnysweb/mic_e/my solutions。

然后Johnysweb还提供了一个解决方案,适用于其他大于2的大小,而其他答案则不适用(好吧,iteration_utilities.grouper还允许将元素数量设置为“group”)。

然后还有一个问题,如果列表中有奇数个元素,会发生什么。剩余的项目是否应该取消?是否应该填充列表以使其大小相等?剩余的物品是否要单独退回?其他答案没有直接解决这一点,但是如果我没有忽略任何东西,他们都遵循剩余的项目应该被驳回的方法(除了taskinoors的答案-这实际上会引发一个异常)。

使用grouper你可以决定你想做什么:

>>> from iteration_utilities import grouper


>>> list(grouper([1, 2, 3], 2))  # as single
[(1, 2), (3,)]


>>> list(grouper([1, 2, 3], 2, truncate=True))  # ignored
[(1, 2)]


>>> list(grouper([1, 2, 3], 2, fillvalue=None))  # padded
[(1, 2), (3, None)]

我需要把一个列表除以一个数字,然后像这样固定。

l = [1,2,3,4,5,6]


def divideByN(data, n):
return [data[i*n : (i+1)*n] for i in range(len(data)//n)]


>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]


>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]

这是一个简单的解决方案,它使用range函数从元素列表中选择替代元素。

注意:这只对偶数列表有效。

a_list = [1, 2, 3, 4, 5, 6]
empty_list = []
for i in range(0, len(a_list), 2):
empty_list.append(a_list[i] + a_list[i + 1])
print(empty_list)
# [3, 7, 11]

使用类型,以便使用mypy静态分析工具验证数据:

from typing import Iterator, Any, Iterable, TypeVar, Tuple


T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]


def legs(iterable: Iterator[T_]) -> Pairs_Iter:
begin = next(iterable)
for end in iterable:
yield begin, end
begin = end

简单的方法:

[(a[i],a[i+1]) for i in range(0,len(a),2)]

如果你的数组是a并且你想对它进行迭代,这很有用。 要遍历三胞胎或更多,只需更改"range" step命令,例如:

[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]

(如果你的数组长度和步长不匹配,你必须处理多余的值)

拆包:

l = [1,2,3,4,5,6]
while l:
i, k, *l = l
print(f'{i}+{k}={i+k}')

注意:这将消耗l,之后将其保留为空。

有很多方法可以做到这一点。例如:

lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]


list(zip(*[iter(lst)]*2))
>>>[(1, 2), (3, 4), (5, 6)]

另一种更清洁的解决方案

def grouped(itr, n=2):
itr = iter(itr)
end = object()
while True:
vals = tuple(next(itr, end) for _ in range(n))
if vals[-1] is end:
return
yield vals

更多定制选项

from collections.abc import Sized


def grouped(itr, n=2, /, truncate=True, fillvalue=None, strict=False, nofill=False):
if strict:
if isinstance(itr, Sized):
if len(itr) % n != 0:
raise ValueError(f"{len(itr)=} is not divisible by {n=}")
itr = iter(itr)
end = object()
while True:
vals = tuple(next(itr, end) for _ in range(n))
if vals[-1] is end:
if vals[0] is end:
return
if strict:
raise ValueError("found extra stuff in iterable")
if nofill:
yield tuple(v for v in vals if v is not end)
return
if truncate:
return
yield tuple(v if v is not end else fillvalue for v in vals)
return
yield vals

经过优化的Python3解决方案在itertools 食谱中给出:

import itertools


def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return itertools.zip_longest(*args, fillvalue=fillvalue)