有没有一种很好的Pythonic方式可以遍历列表,重新调整一对元素?最后一个元素应与第一个元素配对。
因此,例如,如果我有列表[1、2、3],则希望获得以下对:
- 1-2
- 2-3
- 3-1
Answers:
成对访问列表的Python方式是:zip(L, L[1:])。要将最后一项连接到第一项:
>>> L = [1, 2, 3]
>>> zip(L, L[1:] + L[:1])
[(1, 2), (2, 3), (3, 1)]
itertools则可以使用解决方案。
zip(除其他外)非常有效。
zip(*iterables)像3倍的list(zip(*iterables))-这就是为什么我建议导入izip as zip我的回答对Python的2 - stackoverflow.com/a/36918720/541136 - JF却呈Python 2米中的zip这个答案,哪些化合物一长串不必要的创作问题在记忆中。JF的答案使用的内存比列表所需的内存多200%(每项8个字节),以及每个元组的内存(每个元组64个字节的内存相对较大,请参见stackoverflow.com/a/30316760/541136),多800%,因此总计增加1000%(假设懒惰的评估。)
我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)
这只会保留对第一个值的引用,并且当第二个迭代器用尽时, zip_longest将使用第一个值填充最后一个位置。
(还请注意,它可与生成器之类的迭代器以及列表/元组之类的可迭代项一起使用。)
请注意,@ Barry的解决方案与此非常相似,但是在我看来,它有点容易理解,并且更容易扩展到一个元素之外。
cycle。
itertools.zip_longest(a, b, fillvalue=first_value) 较为明显然后izip(a, chain(b, (first,))) -不需要作出额外的元组(first, )
我会itertools.cycle搭配zip:
import itertools
def circular_pairwise(l):
second = itertools.cycle(l)
next(second)
return zip(l, second)
cycle 返回一个迭代器,该迭代器按顺序产生其参数的值,从最后一个值循环到第一个值。
我们跳过第一个值,所以它从位置开始 1(而不是0)。
接下来,我们 zip其与原始的不变列表一起使用。 zip很好,因为它在任何参数可迭代项用尽时都会停止。
这样可以避免创建任何中间列表: cycle保留对原始文档的引用,但不进行复制。 zip以相同的方式操作。
重要的是要注意,如果输入是iterator,例如afile,(或amap或zipinpython-3),因为在某个地方(通过next(second))前进将自动在其他所有地方推进迭代器。使用可以轻松解决此问题itertools.tee,它会在原始可迭代对象上生成两个独立运行的迭代器:
def circular_pairwise(it):
first, snd = itertools.tee(it)
second = itertools.cycle(snd)
next(second)
return zip(first, second)
tee 可以使用大量的额外存储空间,例如,如果一个返回的迭代器在触摸另一个迭代器之前就用完了,但是由于我们只有一步的差异,因此额外的存储空间很小。
itertools,以及一般的函数式编程,它允许非常简洁的代码,同时仍然易于阅读。
有更有效的方法(不构建临时列表),但是我认为这是最简洁的方法:
> l = [1,2,3]
> zip(l, (l+l)[1:])
[(1, 2), (2, 3), (3, 1)]
(l+l)两倍大小的列表。
我将使用列表理解,并利用l[-1]最后一个事实。
>>> l = [1,2,3]
>>> [(l[i-1],l[i]) for i in range(len(l))]
[(3, 1), (1, 2), (2, 3)]
您不需要那种临时列表。
xrange在Python 2中,您需要避免使用临时列表。创建从1到n的数字列表与创建带有n项目的列表的浅表副本一样昂贵。
[(l[i-1], it) for i, it in enumerate(l)]。您可能应该注意,这两种方法都将最后请求的元素放在第一位。
成对循环Python'for'循环
如果您喜欢接受的答案,
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)]
这是难以置信的高效率,的预期使用iter与next和优雅的使用cycle,tee和zip。
不要cycle直接传递给list除非您已保存工作并有足够的时间使计算机在内存最大时停止运行否则请它-如果幸运的话,一段时间后,操作系统会在计算机崩溃之前终止进程。 。
最后,没有标准的lib导入,但这仅适用于原始列表的长度(否则为IndexError)。
>>> [(l[i], l[i - len(l) + 1]) for i in range(len(l))]
[(1, 2), (2, 3), (3, 1)]
您可以使用模来继续:
>>> 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)]
range(10)),否则循环永远不会结束。
即使该列表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]
或更概括地说(适用于任何偏移量,n即l[ (i+n)%len(l) ])
for i in range( len(l)):
pair = l[i], l[ (i+1)%len(l) ]
# stuff
如果您使用的是具有相当快的模除的系统(即,不是一些豆豆嵌入式系统)。
似乎经常有人认为,用整数下标索引列表是非Python的,最好避免。为什么?
for i in range(len(a_list)): print a_list[i]代替编写for x in a_list: print x。
for el in iterable:这是可行的方法。如果还需要索引,enumerate()则是方法。因此,应避免手动索引。
这是我的解决方案,对我来说看起来像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)]
from itertools import izip, chain, islice
itr = izip(l, chain(islice(l, 1, None), islice(l, 1)))
(如上,@ jf-sebastian的“ zip”答案,但使用itertools。)
注意:编辑来自@ 200_success,有帮助。以前是:
itr = izip(l, chain(l[1:], l[:1]))
itertools,那么@RoadieRich的解决方案更好,因为它避免了复制和切片。
L = [1、2、3] a = a中的i的zip(L,L [1:] + L [:1]):b = list(i)打印b
在以后的版本中Python 3.10 release schedule,新pairwise功能提供了一种创建连续元素的滑动对的方法:
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在列表上附加了其head(l + l[:1]),因此滚动对是圆形的(即,因此我们也包括该(3, 1)对):
list(pairwise(l)) # [(1, 2), (2, 3)]
l + l[:1] # [1, 2, 3, 1]
看来组合可以胜任这项工作。
from itertools import combinations
x=combinations([1,2,3],2)
这将产生一个发电机。然后可以这样迭代
for i in x:
print i
结果看起来像
(1, 2)
(1, 3)
(2, 3)
[1,2,3,4,5]到结果[(1,2), (2,3), (3,4), (4,5), (5,1)]不是列表中的组合,而只是组合。