Pairs from single list
通常,我发现需要成对处理一个列表。我想知道哪一种方法是Python式的和有效的,并在谷歌上发现了这一点:
1 | pairs = zip(t[::2], t[1::2]) |
我认为那是Python式的,但在最近一次关于习语和效率的讨论之后,我决定做一些测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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 2 3 4 | 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实现列表、列表索引和列表切片是非常有效的。这是一个既令人欣慰又出人意料的结果。
有没有另一种"更好"的成对遍历列表的方法?
请注意,如果列表中元素的数目是奇数,那么最后一个元素将不在任何对中。
哪种方法是确保所有元素都包含在内的正确方法?
我从测试的答案中添加了这两个建议:
1 2 3 4 5 6 7 | def pairwise(t): it = iter(t) return izip(it, it) def chunkwise(t, size=2): it = iter(t) return izip(*[it]*size) |
结果如下:
1 2 | 0.00159502029419 1.25745987892 1.25586485863 0.00222492218018 1.23795199394 1.23572707176 |
迄今为止的结果
最有毒气和效率的:
1 | pairs = izip(t[::2], t[1::2]) |
最有效和最刺激的Python:
1 | pairs = izip(*[iter(t)]*2) |
我花了一点时间才发现第一个答案使用两个迭代器,而第二个使用一个迭代器。
为了处理元素数目为奇数的序列,建议增加原始序列,添加一个元素(
注意,在python 3.x中,
我最喜欢的方法是:
1 2 3 4 5 6 7 8 9 10 | 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:
1 2 3 4 | from itertools import izip_longest def blockwise(t, size=2, fillvalue=None): it = iter(t) return izip_longest(*[it]*size, fillvalue=fillvalue) |
我认为您的初始解决方案
为了确保包含所有元素,您可以简单地通过
然后,如果列表中元素的数目为奇数,则最后一对将是
1 2 3 4 5 6 7 8 | >>> 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)] |
我从小小的免责声明开始-不要使用下面的代码。根本不是Python,我只是为了好玩才写的。它类似于@thc4k
1 | pairwise = lambda t: iter((lambda f: lambda: (f(), f()))(iter(t).next), None) |
就大多数的Python而言,我认为在python源文档中提供的食谱(其中一些看起来很像@jochenritzel提供的答案)可能是你最好的选择;)
1 2 3 4 5 | 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) |
Is there another,"better" way of traversing a list in pairs?
我不能肯定,但我对此表示怀疑:任何其他的遍历都将包含更多必须解释的Python代码。像zip()这样的内置函数是用C编写的,这要快得多。
Which would be the right way to ensure that all elements are included?
检查列表的长度,如果是奇数(
1 2 3 4 5 6 7 8 | >>> my_list = [1,2,3,4,5,6,7,8,9,10] >>> my_pairs = list() >>> while(my_list): ... a = my_list.pop(0); b = my_list.pop(0) ... my_pairs.append((a,b)) ... >>> print(my_pairs) [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)] |