关于python:迭代列表中的每两个元素

Iterating over every two elements in a list

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

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

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

输出:

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


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

对于python 2:

1
2
3
4
5
6
7
8
9
from itertools import izip

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

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

或者更一般地说:

1
2
3
4
5
6
7
8
from itertools import izip

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 izip(*[iter(iterable)]*n)

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

在python 3中,可以用内置的zip()函数替换izip,并删除import

感谢马蒂诺对我问题的回答,我发现这是非常有效的,因为它只在列表上迭代一次,并且在这个过程中不会创建任何不必要的列表。

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


你需要两个元素的元组,所以

1
2
3
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]表示创建(index % 2 == 0)元素的子集集合。
  • zip(x,y)从x和y集合中创建一个元组集合,该集合与索引元素相同。


1
2
3
4
5
6
7
8
9
10
11
12
13
>>> 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']


一个简单的解决方案。

1
2
3
4
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])


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

1
2
3
4
def pairwise(it):
    it = iter(it)
    while True:
        yield next(it), next(it)

it = iter(it)部分确保it实际上是一个迭代器,而不仅仅是一个iterable。如果it已经是一个迭代器,那么此行是一个no-op。

用途:

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


很抱歉迟到了。我希望这样做会更优雅。

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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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_utilitiesgrouper可能会更快一些。

其他想法

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

例如,一些解决方案只适用于序列(即列表、字符串等),例如margus/pyanon/taskinor解决方案使用索引,而其他解决方案则适用于任何可重复的(即序列和生成器、迭代器),如johnysweb/mic_e/my解决方案。

然后,johnysweb还提供了一个适用于2以外尺寸的解决方案,而其他答案则不适用(好吧,iteration_utilities.grouper还允许将元素数量设置为"group")。

还有一个问题,如果列表中元素的数目是奇数,那么应该发生什么。是否应取消剩余项?应该填充列表使其大小均匀吗?是否应将剩余项作为单个项返回?另一个答案并没有直接解决这一点,但是如果我没有忽略任何问题,他们都会遵循剩下的项目应该被取消的方法(除了taskinoors答案——这实际上会引发一个异常)。

使用grouper,您可以决定要做什么:

1
2
3
4
5
6
7
8
9
10
>>> 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)]


同时使用zipiter命令:

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

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

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

1
2
3
4
5
6
7
it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='
'
)

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

一次概括为N个元素:

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


1
2
for (i, k) in zip(l[::2], l[1::2]):
    print i,"+", k,"=", i+k

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

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

l[1::2]执行相同的操作,但从列表的第二个元素开始,因此返回原始列表的第二个、第四个、第六个等元素。


对于任何人来说,它可能会有所帮助,这里有一个解决类似问题的方法,但是使用重叠对(而不是互斥对)。

从python itertools文档中:

1
2
3
4
5
6
7
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)

或者更一般地说:

1
2
3
4
5
6
7
8
9
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)


您可以使用更多的工具包。

1
2
3
4
5
import more_itertools

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


我需要用一个数字来划分一个列表,然后像这样固定。

1
2
3
4
5
6
7
8
9
10
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]]

一种简单的方法:

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

如果您的数组是一个,并且您希望成对迭代它,那么这很有用。要迭代三元组或更多,只需更改"range"step命令,例如:

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

(如果数组长度和步骤不合适,则必须处理多余的值)


我认为这是一个很好的地方来分享我对n>2的概括,它只是一个滑动窗口在一个不可测的:

1
2
3
4
5
6
def sliding_window(iterable, n):
    its = [ itertools.islice(iter, i, None)
            for i, iter
            in enumerate(itertools.tee(iterable, n)) ]                              

    return itertools.izip(*its)


这个问题的标题是误导性的,您似乎在寻找连续的对,但是如果您想要迭代所有可能对的集合,那么这将起作用:

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


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

1
2
3
4
5
6
7
8
9
10
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


1
2
3
4
5
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)


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

1
2
3
4
5
6
7
8
9
10
11
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)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
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中执行的操作,上述解决方案可能不高效。