遍历列表中的每两个元素


Answers:


231

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

对于Python 2:

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)

或更笼统地说:

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中,您可以替换izip为内置zip()函数,然后删除import

所有信贷蒂诺他的回答我的问题,我发现这是非常有效的,因为它只是在列表上循环一次,并在此过程中不会产生任何不必要的名单。

注意:不要将其与Python自己的文档中pairwise配方混淆,后者由@lazyr在评论中指出。itertoolss -> (s0, s1), (s1, s2), (s2, s3), ...

对于想要在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)

16
不要与itertools配方部分中建议的成对函数混淆,该函数会产生s -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow

1
它做不同的事情。itertools与具有相同名称的配方函数相比,您的版本仅产生一半的对。当然,您的速度更快...
Sven Marnach 2011年

??您的功能和我提到的功能有不同的作用,这就是我评论的重点。
Lauritz V. Thaulow 2011年

5
小心!使用这些功能会使您面临无法迭代可迭代对象的最后元素的风险。示例:list(grouped([[1,2,3],2))>>> [(1,2)] ..当您期望[[1,2 ,,(3,)]
egafni

4
@ Erik49:在问题中指定的情况下,具有“不完整”元组没有任何意义。如果要包含不完整的元组,可以使用izip_longest()代替izip()。例如:list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]。希望这可以帮助。
Johnsyweb

191

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

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集合的相同索引元素创建一个元组集合。

8
如果需要两个以上的元素,这也可以扩展。例如for i, j, k in zip(data[0::3], data[1::3], data[2::3]):
生命平衡2014年

19
比导入和定义函数要干净得多!
kmarsh 2014年

7
@kmarsh:但这仅适用于序列,该函数适用于任何可迭代的函数;并且这会使用O(N)多余的空间,而函数不会;另一方面,这通常更快。有充分的理由选择其中一个。害怕import不是其中之一。
2014年

77
>>> 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
这不适用于Python-3.6.0,但仍适用于Python-2.7.10
Hamid Rohani

6
@HamidRohani 在Python 3中zip返回一个zip不可下标的对象。首先需要将其转换为序列(listtuple等),但是“无效”有点麻烦。
vaultah '17

58

一个简单的解决方案。

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

对于范围(0,len(l),2)中的i:
    打印str(l [i]),'+',str(l [i + 1]),'=',str(l [i] + l [i + 1])

1
如果您的列表不是偶数,而您只想显示最后一个数字怎么办?
Hans de Jong

@HansdeJong没有得到你。请多解释一点。
taskinoor 2014年

2
谢谢。我已经想出了怎么做。问题是,如果您的列表中没有数字,则将出现索引错误。尝试解决该问题:例外:
Hans de Jong

或者((l[i], l[i+1])for i in range(0, len(l), 2))对于生成器,可以很容易地对其进行修改以使用更长的元组。
巴塞尔Shishani,2015年

44

尽管使用的所有答案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)

2
此解决方案允许将元组的大小推广到> 2
guilloptero 2015年

1
如果it仅是迭代器而不是可迭代的,则此解决方案也适用。其他解决方案似乎依赖于为序列创建两个独立的迭代器的可能性。
2015年

在看到此答案之前,我在stackoverflow.com/a/16815056/2480481找到了这种方法。比使用zip()更干净,更容易。
m3nda

2
我喜欢这样做,可以避免将内存使用量增加三倍,这是公认的答案。
肯佐(Kentzo)'18

for由于PEP 479,这在Python 3.5+中的循环中不能很好地工作,它用代替StopIteration了生成器中引发的任何循环RuntimeError
悉尼,

28

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

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

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

18

如果您对性能感兴趣,我做了一个小型基准测试(使用我的library 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)

在此处输入图片说明

因此,如果您想要没有外部依赖项的最快解决方案,则可能应该使用Johnysweb给出的方法(在撰写本文时,它是最受支持和接受的答案)。

如果您不介意其他依赖项,那么grouperfrom iteration_utilities可能会更快。

其他想法

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

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

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

还有一个问题是,如果列表中的元素数量奇数,应该怎么办。剩余的物品应否开除?是否应该填充列表以使其大小均匀?剩余的物品是否应作为单一物品退还?另一个答案不能直接解决这一点,但是,如果我没有忽略任何内容,那么他​​们都会遵循将剩余项目排除的方法(taskinoor答案除外-这实际上会引发异常)。

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

12

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

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

zip(*iterable) 返回一个元组以及每个可迭代的下一个元素。

l[::2] 返回列表的1、3、5等元素:第一个冒号表示切片从开头开始,因为后面没有数字,仅当您要在切片中使用“ step”时,才需要第二个冒号'(在这种情况下为2)。

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


4
玛格斯两年前已经给出了这个答案。stackoverflow.com/questions/5389507/…–
cababunga

1
1,用于解释[number::number]语法的工作原理。对于谁不经常使用python很有帮助
Alby

2

带包装:

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

哇!为什么我不能考虑这个问题:)您需要处理没有绝对对(奇数项)的情况
Saurav Kumar

1

对于任何人都可能有帮助,这是解决类似问题的解决方案,但对具有重叠对(而不是互斥对)。

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

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

1

我需要将列表除以数字并固定不变。

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

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

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

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

0

认为这是分享我对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)

0

这个问题的标题具有误导性,您似乎正在寻找连续对,但如果要遍历所有可能对的集合,则可以使用以下方法:

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

0

使用输入法,因此您可以使用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

0

一种简单的方法:

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

如果您的数组是a并且您想成对迭代它,这将很有用。要遍历三胞胎或更多,只需更改“ range”步骤命令,例如:

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

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


-1

在这里,我们可以找到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中执行的操作,上述解决方案可能不是有效的。


-1
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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.