双从单个列表

通常情况下,我发现需要成对处理一个列表。 我想知道哪种方法可以做到这一点,并在Google上发现:

pairs = zip(t[::2], t[1::2]) 

我认为这是足够pythonic,但经过最近的讨论涉及成语与效率 ,我决定做一些testing:

 import time from itertools import islice, izip def pairs_1(t): return zip(t[::2], t[1::2]) def pairs_2(t): return izip(t[::2], t[1::2]) def pairs_3(t): return izip(islice(t,None,None,2), islice(t,1,None,2)) A = range(10000) B = xrange(len(A)) def pairs_4(t): # ignore value of t! t = B return izip(islice(t,None,None,2), islice(t,1,None,2)) for f in pairs_1, pairs_2, pairs_3, pairs_4: # time the pairing s = time.time() for i in range(1000): p = f(A) t1 = time.time() - s # time using the pairs s = time.time() for i in range(1000): p = f(A) for a, b in p: pass t2 = time.time() - s print t1, t2, t2-t1 

这些是我电脑上的结果:

 1.48668909073 2.63187503815 1.14518594742 0.105381965637 1.35109519958 1.24571323395 0.00257992744446 1.46182489395 1.45924496651 0.00251388549805 1.70076990128 1.69825601578 

如果我正确地解释它们,这应该意味着在Python中实现列表,列表索引和列表切片是非常有效的。 这是一个既舒适又意外的结果。

还有另外一种“更好”的方式来成对遍历一个列表吗?

请注意,如果列表中有奇数个元素,则最后一个元素将不在任何一个元素中。

哪一个是确保包含所有元素的正确方法?

我从testing的答案中添加了这两个build议:

 def pairwise(t): it = iter(t) return izip(it, it) def chunkwise(t, size=2): it = iter(t) return izip(*[it]*size) 

这是结果:

 0.00159502029419 1.25745987892 1.25586485863 0.00222492218018 1.23795199394 1.23572707176 

目前为止的结果

最pythonic和非常有效的:

 pairs = izip(t[::2], t[1::2]) 

最高效和非常pythonic:

 pairs = izip(*[iter(t)]*2) 

我花了一点时间,第一个答案使用两个迭代器,而第二个使用一个。

为了处理具有奇数个元素的序列,build议是增加原始序列,添加一个元素( None ),与前一个元素配对,这可以通过itertools.izip_longest()来实现。

最后

请注意,在Python 3.x中, zip()行为与itertools.izip() ,而itertools.izip()则消失。

我最喜欢的做法是:

 from itertools import izip def pairwise(t): it = iter(t) return izip(it,it) # for "pairs" of any length def chunkwise(t, size=2): it = iter(t) return izip(*[it]*size) 

当你想要配对所有元素时,显然可能需要一个fillvalue:

 from itertools import izip_longest def blockwise(t, size=2, fillvalue=None): it = iter(t) return izip_longest(*[it]*size, fillvalue=fillvalue) 

我会说,你最初的解决schemepairs = zip(t[::2], t[1::2])是最好的,因为它是最容易阅读的(在Python 3中, zip自动返回一个迭代器,而不是一个列表)。

为确保所有元素都包含在内,您可以简单地将这个列表扩展为None

然后,如果列表中有奇数个元素,最后一对将是(item, None)

 >>> t = [1,2,3,4,5] >>> t.append(None) >>> zip(t[::2], t[1::2]) [(1, 2), (3, 4), (5, None)] >>> t = [1,2,3,4,5,6] >>> t.append(None) >>> zip(t[::2], t[1::2]) [(1, 2), (3, 4), (5, 6)] 

我从小的免责声明开始 – 不要使用下面的代码。 根本不是Pythonic,我只是为了好玩而写的。 它类似于@ THC4k pairwise函数,但它使用iterlambdaclosures。 它不使用itertools模块,不支持fillvalue 。 我把它放在这里,因为有人可能觉得它很有趣:

 pairwise = lambda t: iter((lambda f: lambda: (f(), f()))(iter(t).next), None) 

还有另外一种“更好”的方式来成对遍历一个列表吗?

我不能肯定地说,但我怀疑它:任何其他遍历将包括更多的Python代码,必须解释。 像zip()这样的内置函数是用C语言编写的,速度要快得多。

哪一个是确保包含所有元素的正确方法?

检查列表的长度,如果它是奇数( len(list) & 1 == 1 ),复制列表并追加一个项目。

就大多数pythonic而言,我会说在python源文档中提供的食谱 (其中一些看起来很像@JochenRitzel提供的答案)可能是您最好的select;)

 def grouper(iterable, n, fillvalue=None): "Collect data into fixed-length chunks or blocks" # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx args = [iter(iterable)] * n return izip_longest(fillvalue=fillvalue, *args)