成对循环Python'for'循环

时间:2016-04-28 14:02:56

标签: python

是否有一种漂亮的Pythonic方法循环遍历列表,重新调整一对元素?最后一个元素应与第一个元素配对。

例如,如果我有列表[1,2,3],我想获得以下对:

  • 1 - 2
  • 2 - 3
  • 3 - 1

18 个答案:

答案 0 :(得分:110)

成对访问列表的Pythonic方法是:zip(L, L[1:])。要将最后一项连接到第一项:

>>> L = [1, 2, 3]
>>> zip(L, L[1:] + L[:1])
[(1, 2), (2, 3), (3, 1)]

答案 1 :(得分:49)

我会使用dequezip来实现这一目标。

>>> from collections import deque
>>>
>>> l = [1,2,3]
>>> d = deque(l)
>>> d.rotate(-1)
>>> zip(l, d)
[(1, 2), (2, 3), (3, 1)]

答案 2 :(得分:44)

我对itertools documentation中的pairwise食谱略作修改:

def pairwise_circle(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ... (s<last>,s0)"
    a, b = itertools.tee(iterable)
    first_value = next(b, None)
    return itertools.zip_longest(a, b,fillvalue=first_value)

这将只保留对第一个值的引用,当第二个迭代器耗尽时,zip_longest将使用第一个值填充最后一个位置。

(另请注意,它适用于像生成器这样的迭代器以及列表/元组之类的迭代。)

请注意@Barry's solution与此非常相似,但在我看来更容易理解,更容易扩展到一个元素之外。

答案 3 :(得分:38)

我会将itertools.cyclezip配对:

import itertools

def circular_pairwise(l):
    second = itertools.cycle(l)
    next(second)
    return zip(l, second)

cycle返回一个迭代,它按顺序产生其参数的值,从最后一个值循环到第一个值。

我们跳过第一个值,因此它从位置1(而不是0)开始。

接下来,我们zip使用原始的未突变列表。 zip很好,因为当它的任何参数可迭代用尽时它会停止。

这样做可以避免创建任何中间列表:cycle包含对原始列表的引用,但不会复制它。 zip以相同的方式运作。

重要的是要注意,如果输入为iterator,例如file,或mapzip,这将会中断),因为在一个地方前进(通过next(second))将自动推进所有其他地方的迭代器。这可以使用itertools.tee轻松解决,{{3}}在原始迭代上生成两个独立运算的迭代器:

def circular_pairwise(it):
    first, snd = itertools.tee(it)
    second = itertools.cycle(snd)
    next(second)
    return zip(first, second)

tee 可以使用大量的额外存储空间,例如,如果其中一个返回的迭代器在触摸另一个之前用完了,但是因为我们只有一个步骤差异,额外的存储空间很小。

答案 4 :(得分:28)

有更有效的方法(不构建临时列表),但我认为这是最简洁的方法:

> l = [1,2,3]
> zip(l, (l+l)[1:])
[(1, 2), (2, 3), (3, 1)]

答案 5 :(得分:22)

我会使用列表理解,并利用l[-1]是最后一个元素的事实。

>>> l = [1,2,3]
>>> [(l[i-1],l[i]) for i in range(len(l))]
[(3, 1), (1, 2), (2, 3)]

您不需要这样的临时列表。

答案 6 :(得分:21)

  

成对循环Python'用于'循环

如果您喜欢接受的答案,

zip(L, L[1:] + L[:1])

使用itertools

,您可以使用语义相同的代码获得更多内存灯
from itertools import islice, chain #, izip as zip # uncomment if Python 2

这几乎没有在原始列表之外的内存中实现任何内容(假设列表相对较大):

zip(l, chain(islice(l, 1, None), islice(l, None, 1)))

要使用,只需消费(例如,使用列表):

>>> list(zip(l, chain(islice(l, 1, None), islice(l, None, 1))))
[(1, 2), (2, 3), (3, 1)]

这可以扩展到任何宽度:

def cyclical_window(l, width=2):
    return zip(*[chain(islice(l, i, None), islice(l, None, i)) for i in range(width)])

和用法:

>>> l = [1, 2, 3, 4, 5]
>>> cyclical_window(l)
<itertools.izip object at 0x112E7D28>
>>> list(cyclical_window(l))
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)]
>>> list(cyclical_window(l, 4))
[(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 1), (4, 5, 1, 2), (5, 1, 2, 3)]

使用itertools.tee

cycle进行无限制生成

您还可以使用tee来避免创建冗余循环对象:

from itertools import cycle, tee
ic1, ic2 = tee(cycle(l))
next(ic2)    # must still queue up the next item

现在:

>>> [(next(ic1), next(ic2)) for _ in range(10)]
[(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]

这非常高效,iternext的预期用量,以及cycleteezip的优雅用法。

不要将cycle直接传递给list,除非你已经保存了你的工作并且有时间让你的计算机在你的记忆最大化时停止 - 如果你很幸运,之后一段时间,您的操作系统会在崩溃您的计算机之前终止该进程。

纯Python内置函数

最后,没有标准的lib导入,但这仅适用于原始列表的长度(否则为IndexError。)

>>> [(l[i], l[i - len(l) + 1]) for i in range(len(l))]
[(1, 2), (2, 3), (3, 1)]

您可以使用modulo继续:

>>> len_l = len(l)
>>> [(l[i % len_l], l[(i + 1) % len_l]) for i in range(10)]
[(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]

答案 7 :(得分:19)

令人惊讶的是有多少种方法可以解决这个问题。

还有一个。您可以使用pairwise配方,而不是使用bchain与您已经弹出的第一个元素进行压缩。当我们只需要一个额外的值时,不需要cycle

from itertools import chain, izip, tee

def pairwise_circle(iterable):
    a, b = tee(iterable)
    first = next(b, None)
    return izip(a, chain(b, (first,)))

答案 8 :(得分:11)

我喜欢不修改原始列表的解决方案,也不会将列表复制到临时存储中:

void Circle::print() {
    Shape::print();
    // ... etc.
}

输出:

def circular(a_list):
    for index in range(len(a_list) - 1):
        yield a_list[index], a_list[index + 1]
    yield a_list[-1], a_list[0]

for x in circular([1, 2, 3]):
    print x

我可以想象这被用在一些非常大的内存数据中。

答案 9 :(得分:8)

即使列表l.append( l[0] ) for i in range( len(l)-1): pair = l[i],l[i+1] # stuff involving pair del l[-1] 占用了系统的大部分内存,这个也能正常工作。 (如果有什么东西可以保证这种情况不可能,那么由chepner发布的zip很好)

n

或更普遍(适用于任何偏移l[ (i+n)%len(l) ],即for i in range( len(l)): pair = l[i], l[ (i+1)%len(l) ] # stuff

<Set name="port"><Property name="jetty.port" default="8085"/></Set> 

如果您使用的是具有相当快速的模数除法的系统(即不是某些豌豆脑的嵌入式系统)。

似乎有一种常见的信念,即使用整数下标对列表进行索引是非pythonic并且最好避免。为什么?

答案 10 :(得分:7)

这是我的解决方案,看起来Pythonic对我来说足够了:

l = [1,2,3]

for n,v in enumerate(l):
    try:
        print(v,l[n+1])
    except IndexError:
        print(v,l[0])

打印:

1 2
2 3
3 1

生成器功能版本:

def f(iterable):
    for n,v in enumerate(iterable):
        try:
            yield(v,iterable[n+1])
        except IndexError:
            yield(v,iterable[0])

>>> list(f([1,2,3]))
[(1, 2), (2, 3), (3, 1)]

答案 11 :(得分:6)

这个怎么样?

li = li+[li[0]]
pairwise = [(li[i],li[i+1]) for i in range(len(li)-1)]

答案 12 :(得分:5)

from itertools import izip, chain, islice

itr = izip(l, chain(islice(l, 1, None), islice(l, 1)))

(如上所述@j-f-sebastian's "zip" answer,但使用itertools。)

NB:已编辑,可以从@200_success获得有用的推动。以前是:

itr = izip(l, chain(l[1:], l[:1]))

答案 13 :(得分:3)

再试一次

>>> L = [1,2,3]
>>> zip(L,L[1:]) + [(L[-1],L[0])]
[(1, 2), (2, 3), (3, 1)]

答案 14 :(得分:3)

如果您不想消耗太多内存,可以尝试我的解决方案:

[(l[i], l[(i+1) % len(l)]) for i, v in enumerate(l)]

它有点慢,但消耗的内存较少。

答案 15 :(得分:2)

L = [1,2,3] a = zip(L,L [1:] + L [:1]) 对于我:     b =清单(i)     打印b

答案 16 :(得分:1)

在即将到来的add release schedule 中,新的pairwise函数提供了一种创建连续元素的滑动对的方法:

Python 3.10

,或者如果您不需要将结果作为from itertools import pairwise # l = [1, 2, 3] list(pairwise(l + l[:1])) # [(1, 2), (2, 3), (3, 1)] ,则只需pairwise(l + l[:1])


请注意,我们在列表上list的后面加上了pairwise,以便滚动对是圆形的(即,我们还包括l + l[:1]对):

(3, 1)

答案 17 :(得分:0)

这看起来像组合会起作用。

(^|\n)[^\S\r\n]+

这将产生一个发电机。然后可以这样迭代

from itertools import combinations
x=combinations([1,2,3],2)

结果看起来像

for i in x:
  print i