成对循环Python'for'循环

The*_*ler 75 python

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

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

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

jfs*_*jfs 111

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

>>> L = [1, 2, 3]
>>> zip(L, L[1:] + L[:1])
[(1, 2), (2, 3), (3, 1)]
Run Code Online (Sandbox Code Playgroud)

  • 如果列表很大,这可能是不可取的 (32认同)
  • @AaronHall正确.但请记住:["过早优化是所有邪恶的根源"](http://c2.com/cgi/wiki?PrematureOptimization).如果内存是一个问题或支持任意迭代(不仅仅是序列),可以使用[`itertools`解决方案.](http://stackoverflow.com/a/36917655/4279) (9认同)
  • @SeldomNeedy Python 2的`zip(*iterables)`就像3的`list(zip(*iterables))` - 这就是为什么我建议在我的答案中为Python 2导入`izip as zip` - http://stackoverflow.com/a/36918720/541136 - JF在这个答案中实际上假设了Python 2的`zip`,这加剧了在内存中不必要地创建长列表的问题.JF的答案使用的内存比列表所需的内存多200%(每个项目8个字节),以及每个元组的内存(每个元组64字节相对较大,请参阅http://stackoverflow.com/a/30316760/541136) 800%以上,总计1000%以上(假设懒惰评价.) (5认同)

g.d*_*d.c 49

我会使用dequewith zip来实现这一点.

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


Tad*_*sen 44

我会对文档中pairwise配方稍作修改:itertools

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)
Run Code Online (Sandbox Code Playgroud)

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

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

请注意,@ Barry的解决方案与此非常相似,但在我看来更容易理解,更容易扩展到一个元素之外.


Roa*_*ich 38

我会itertools.cycle搭配zip:

import itertools

def circular_pairwise(l):
    second = itertools.cycle(l)
    next(second)
    return zip(l, second)
Run Code Online (Sandbox Code Playgroud)

cycle 返回一个iterable,它按顺序生成其参数的值,从最后一个值循环到第一个值.

我们跳过第一个值,所以它从位置开始1(而不是0).

接下来,我们zip使用原始的,未列出的列表. zip很好,因为当它的任何参数迭代都用完时它会停止.

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

需要注意的是,这将打破,如果输入的是一个很重要的iterator,比如file,(或mapzip),如在一个地方(通过推进next(second))会自动前进迭代器中的所有其他人.这很容易使用itertools.tee,它在原始的iterable上生成两个独立运算的迭代器:

def circular_pairwise(it):
    first, snd = itertools.tee(it)
    second = itertools.cycle(snd)
    next(second)
    return zip(first, second)
Run Code Online (Sandbox Code Playgroud)

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

  • [循环`docs](https://docs.python.org/2/library/itertools.html#itertools.cycle)明确声明他们在输入可迭代中保存每个项目的副本 - 所以你是制作列表副本不会节省任何空间.其他几个答案使用`chain`或`fillvalue`来避免这个问题. (3认同)

che*_*ner 28

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

> l = [1,2,3]
> zip(l, (l+l)[1:])
[(1, 2), (2, 3), (3, 1)]
Run Code Online (Sandbox Code Playgroud)

  • 如果列表真的很长,这是一个问题.它创建了另一个列表`(l + l)`两倍的大小. (7认同)
  • "如果名单很长,这就是一个问题." 创建另一个列表,无论长期与否,本身并不是问题; 这是一个需要注意的事实. (3认同)

Atn*_*Atn 22

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

>>> l = [1,2,3]
>>> [(l[i-1],l[i]) for i in range(len(l))]
[(3, 1), (1, 2), (2, 3)]
Run Code Online (Sandbox Code Playgroud)

你不需要这样的临时列表.

  • @chepner:我没有指定时会假设python3;) (6认同)
  • 我认为与你自己的答案太相似了,这也可能就像`[(l [i-1],it)对于i,它在枚举(l)]中.您应该注意,这两种方法都首先要求最后一个元素. (5认同)
  • 你需要在Python 2中使用`xrange`来避免临时列表.创建从1到n的数字列表与使用`n`项创建列表的浅表副本一样昂贵. (2认同)

Aar*_*all 22

成对循环Python'for'循环

如果你喜欢接受的答案,

zip(L, L[1:] + L[:1])
Run Code Online (Sandbox Code Playgroud)

您可以使用以下语义在语义上使用itertools以下代码获取更多内存灯:

from itertools import islice, chain #, izip as zip # uncomment if Python 2
Run Code Online (Sandbox Code Playgroud)

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

zip(l, chain(islice(l, 1, None), islice(l, None, 1)))
Run Code Online (Sandbox Code Playgroud)

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

>>> list(zip(l, chain(islice(l, 1, None), islice(l, None, 1))))
[(1, 2), (2, 3), (3, 1)]
Run Code Online (Sandbox Code Playgroud)

这可以扩展到任何宽度:

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

和用法:

>>> 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)]
Run Code Online (Sandbox Code Playgroud)

无限生成itertools.teecycle

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

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

现在:

>>> [(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)]
Run Code Online (Sandbox Code Playgroud)

这是难以置信的高效率,的预期使用iternext和优雅的使用cycle,teezip.

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

纯Python内置函数

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

>>> [(l[i], l[i - len(l) + 1]) for i in range(len(l))]
[(1, 2), (2, 3), (3, 1)]
Run Code Online (Sandbox Code Playgroud)

您可以使用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)]
Run Code Online (Sandbox Code Playgroud)

  • 当然它们会结束,只是将它们传递给列表,当你的内存耗尽时它们就会结束.:d (4认同)

Bar*_*rry 19

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

还有一个.你可以使用pairwise配方而不是拉链b,chain它与你已经弹出的第一个元素.不需要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,)))
Run Code Online (Sandbox Code Playgroud)


Dav*_*len 11

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

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
Run Code Online (Sandbox Code Playgroud)

输出:

(1, 2)
(2, 3)
(3, 1)
Run Code Online (Sandbox Code Playgroud)

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


nig*_*222 8

即使列表l占用了系统的大部分内存,这个也可以工作.(如果有什么东西可以保证这种情况不可能,那么由chepner发布的zip很好)

l.append( l[0] )
for i in range( len(l)-1):
   pair = l[i],l[i+1]
   # stuff involving pair
del l[-1] 
Run Code Online (Sandbox Code Playgroud)

或更普遍(适用于任何偏移,nl[ (i+n)%len(l) ])

for i in range( len(l)):
   pair = l[i], l[ (i+1)%len(l) ]
   # stuff
Run Code Online (Sandbox Code Playgroud)

如果你在一个具有相当快速的模数除法的系统上(即不是一些豌豆脑的嵌入式系统).

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

  • 你最后一句话应该是一个单独的问题.答案是来自C,C++或Java的新Python程序员在范围内编写`for i(len(a_list)):在a_list中打印a_list [i]`而不是`for x:print x`. (2认同)

ale*_*inn 7

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

l = [1,2,3]

for n,v in enumerate(l):
    try:
        print(v,l[n+1])
    except IndexError:
        print(v,l[0])
Run Code Online (Sandbox Code Playgroud)

打印:

1 2
2 3
3 1
Run Code Online (Sandbox Code Playgroud)

生成器功能版本:

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)]
Run Code Online (Sandbox Code Playgroud)


Asw*_*P J 6

这个怎么样?

li = li+[li[0]]
pairwise = [(li[i],li[i+1]) for i in range(len(li)-1)]
Run Code Online (Sandbox Code Playgroud)


sha*_*unc 5

from itertools import izip, chain, islice

itr = izip(l, chain(islice(l, 1, None), islice(l, 1)))
Run Code Online (Sandbox Code Playgroud)

(如上所述@ jf-sebastian的"zip"答案,但使用itertools.)

注:编辑给出有用的微调从@ 200_success.以前是:

itr = izip(l, chain(l[1:], l[:1]))
Run Code Online (Sandbox Code Playgroud)