将列表分组为n的替代方法

85 投票
6 回答
105997 浏览
提问于 2025-04-15 15:23

假设我有一个任意长度的列表,叫做 L:

L = list(range(1000))

我想知道把这个列表分成每组 n 个元素的最佳方法是什么?这是我目前想到的最佳结构,但总觉得这不是完成这个任务的最好方式:

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

有没有我遗漏的内置方法可以做到这一点?

编辑:早期的回答把我的 for 循环改成了列表推导式,这不是我想要的;你们基本上是把我的答案换了一种形式给我。我想看看有没有其他方法可以实现这个,比如说在列表上使用一个假想的 .split 方法之类的。我昨晚写的代码中也确实用到了这个作为生成器:

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]

6 个回答

80

这样怎么样:

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

给你看看:

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

举个例子:

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

如果元素的数量不能被N整除,但你还是想把它们都包含进来,可以使用izip_longest,不过这个功能从Python 2.6版本才开始有。

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

这个结果是一个生成器,所以如果你想打印出来,就需要把它转换成一个列表。

最后,如果你没有Python 2.6,仍在使用旧版本,但又想得到同样的结果,可以使用map:

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

我想补充一下不同方法之间的速度比较:

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_longest和map要慢。

56

这是一个关于Python的示例代码(在Python 2.6中,可以使用itertools.izip_longest):

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

下面是一个使用示例:

>>> 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来填充,那么你可以像这样修改代码:

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

撰写回答