关于python:将列表拆分为n组的替代方法

Alternative way to split a list into groups of n

本问题已经有最佳答案,请猛点这里访问。

假设我有一个任意长度的列表,l:

1
L = list(range(1000))

将该列表分成多个组的最佳方法是什么?这是我能想到的最好的结构,出于某种原因,我觉得这不是完成任务的最佳方式:

1
2
3
n = 25
for i in range(0, len(L), n):
    chunk = L[i:i+25]

有没有一个内置的来做这个我不见了?

编辑:早期的答案是把我的for循环改写成一个listcomp,这不是一个主意;你基本上是以不同的形式给我一个准确的答案。我在看是否有其他的方法来实现这一点,比如清单上的一个假设的.split之类的。在我昨晚写的一些代码中,我也使用它作为生成器:

1
2
3
4
def split_list(L, n):
    assert type(L) is list,"L is not a list"
    for i in range(0, len(L), n):
        yield L[i:i+n]


干得好:

1
list_of_groups = zip(*(iter(the_list),) * group_size)

例子:

1
2
print zip(*(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]

如果元素的数量不能被n整除,但您仍然希望包含它们,那么可以使用izip_longst,但它仅在python 2.6之后可用。

1
izip_longest(*(iter(range(10)),) * 3)

结果是一个生成器,因此如果要打印它,需要将其转换为列表。

最后,如果您没有python2.6,并且坚持使用旧版本,但仍然希望获得相同的结果,那么可以使用map:

1
2
print map(None, *(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]

我想在目前介绍的不同方法之间添加一些速度比较:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
python -m timeit -s 'from itertools import izip_longest; L = range(1000)' 'list(izip_longest(*(iter(L),) * 3))'
10000 loops, best of 3: 47.1 usec per loop

python -m timeit -s 'L = range(1000)' 'zip(*(iter(L),) * 3)'
10000 loops, best of 3: 50.1 usec per loop

python -m timeit -s 'L = range(1000)' 'map(None, *(iter(L),) * 3)'
10000 loops, best of 3: 50.7 usec per loop

python -m timeit -s 'L = range(1000)' '[L[i:i+3] for i in range(0, len(L), 3)]'
10000 loops, best of 3: 157 usec per loop

python -m timeit -s 'import itertools; L = range(1000)' '[list(group) for key, group in itertools.groupby(L, lambda k: k//3)]'
1000 loops, best of 3: 1.41 msec per loop

列表理解和分组方法明显慢于zip、izip-long和map。


怎么样:

1
2
3
4
>>> n = 2
>>> l = [1,2,3,4,5,6,7,8,9]
>>> [ l[i:i+n] for i in range(0, len(l), n) ]
[[1, 2], [3, 4], [5, 6], [7, 8], [9]]


一个python配方(在python 2.6中,使用itertools.izip_longest):

1
2
3
4
def grouper(n, iterable, fillvalue=None):
   "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.zip_longest(*args, fillvalue=fillvalue)

示例用法:

1
2
3
4
>>> list(grouper(3, range(9)))
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]
>>> list(grouper(3, range(10)))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]

如果您希望最后一组比其他组短,而不是用fillvalue填充,那么您可以这样更改代码:

1
2
3
4
5
6
7
8
>>> def mygrouper(n, iterable):
...     args = [iter(iterable)] * n
...     return ([e for e in t if e != None] for t in itertools.zip_longest(*args))
...
>>> list(mygrouper(3, range(9)))
[[0, 1, 2], [3, 4, 5], [6, 7, 8]]
>>> list(mygrouper(3, range(10)))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]


itertools.groupby是一个很好的工具,下面是一种简单地使用整数除法拆分整数列表的方法:

1
2
3
4
5
6
7
>>> for key, group in itertools.groupby(range(10), lambda k: k//3):
...  print key, list(group)
...
0 [0, 1, 2]
1 [3, 4, 5]
2 [6, 7, 8]
3 [9]

(列表必须以0开头,才能以完整组开头。)


1
2
n = 25    
list_of_lists = [L[i:i+n] for i in range(0, len(L), n)]

它为您提供了列表[[0..24], [25..49], ..]

如果len(L) % n不是0,则最后一个元素(list_of_lists[-1]的长度将为len(l)%n。


这里是递归版本。它效率很低,因为Python有递归限制,但是这个版本说明了每个任务都可以通过递归来解决。

1
2
3
4
5
6
7
8
def split_to_groups(l, n):
    assert (len(l) / n) < 998,"Can't split to {} groups".format(len(l) / n)
    if l == []:
        return []
    else:
        f = [l[:n]]
        f.extend(split_to_groups(l[n:], n))
        return f