双从单个列表
通常情况下,我发现需要成对处理一个列表。 我想知道哪种方法可以做到这一点,并在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
函数,但它使用iter
和lambda
closures。 它不使用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)