单列表中的对

很多时候,我发现需要成对地处理一个列表。我想知道哪种方式更简洁、更有效,于是在谷歌上找到了这个:

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

我认为这已经足够了,但在最近一次有关 idioms versus efficiency的讨论之后,我决定做一些测试:

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

These were the results on my computer:

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 中实现列表、列表索引和列表切片是非常有效的。这是一个既令人欣慰又出乎意料的结果。

有没有另一种“更好”的方法来成对遍历列表?

Note that if the list has an odd number of elements then the last one will not be in any of the pairs.

确保所有元素都包含在内的正确方法是什么?

我从测试的答案中添加了以下两条建议:

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

目前结果如何

最简洁,最有效率:

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

最有效率,也是最简洁的:

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

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

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

终于来了

Note that, in Python 3.x, zip() behaves as itertools.izip(), and itertools.izip() is gone.

80344 次浏览

Is there another, "better" way of traversing a list in pairs?

我不能肯定地说,但我对此表示怀疑: 任何其他的遍历都会包含更多需要解释的 Python 代码。像 zip ()这样的内置函数是用 C 语言编写的,速度快得多。

确保所有元素都包含在内的正确方法是什么?

Check the length of the list and if it's odd (len(list) & 1 == 1), copy the list and append an item.

我认为最初的解决方案 pairs = 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)]

我最喜欢的方式是:

def pairwise(t):
it = iter(t)
return zip(it,it)


# for "pairs" of any length
def chunkwise(t, size=2):
it = iter(t)
return zip(*[it]*size)

当您想要将所有元素配对时,显然可能需要一个填充值:

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

对于 Python3,itertools.izip现在只是简单的 zip. . 来处理较老的 Python,使用

from itertools import izip as zip

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

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

就大多数 pythonic 而言,我认为 Python 源文档中提供的菜谱(其中一些看起来很像@JochenRitzel 提供的答案)可能是最好的选择;)

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)

On modern python you just have to use zip_longest(*args, fillvalue=fillvalue) 根据相应的文档页面.

>>> 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)]

Here is an example of creating pairs/legs by using a generator. Generators are free from stack limits

def pairwise(data):
zip(data[::2], data[1::2])

例如:

print(list(pairwise(range(10))))

Output:

[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]

只有这样做:

>>> l = [1, 2, 3, 4, 5, 6]
>>> [(x,y) for x,y in zip(l[:-1], l[1:])]
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

为了以防有人需要算法方面的答案,这里是:

>>> def getPairs(list):
...     out = []
...     for i in range(len(list)-1):
...         a = list.pop(0)
...         for j in a:
...             out.append([a, j])
...     return b
>>>
>>> k = [1, 2, 3, 4]
>>> l = getPairs(k)
>>> l
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

但是请注意,您的原始列表也将减少到它的最后一个元素,因为您在它上面使用了 pop

>>> k
[4]

这个片段对我很有用。如果列表长度为奇数(fillvalue="") ,它会创建一对元组并将空字符串添加到最后一对元组。

zip_longest(*[iter(my_list)] * 2, fillvalue="")


# odd
list(zip_longest(*[iter([0, 1, 2, 3, 4, 5, 6])] * 2, fillvalue=""))
[(0, 1), (2, 3), (4, 5), (6, '')]


# even
list(zip_longest(*[iter([0, 1, 2, 3, 4, 5])] * 2, fillvalue=""))
[(0, 1), (2, 3), (4, 5)]