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

Jed*_*ith 54 python

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

L = list(range(1000))
Run Code Online (Sandbox Code Playgroud)

将该列表拆分成组的最佳方法是n什么?这是我能够提出的最好的结构,并且由于某种原因,它不觉得它是完成任务的最佳方式:

n = 25
for i in range(0, len(L), n):
    chunk = L[i:i+25]
Run Code Online (Sandbox Code Playgroud)

是否有内置功能,我缺少这个?

编辑:早期的答案是将我的for循环重新编写为listcomp,这不是主意; 你基本上以不同的形式给我我的确切答案.我正在看是否有另一种方法可以实现这一点,就像.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]
Run Code Online (Sandbox Code Playgroud)

Nad*_*mli 117

干得好:

list_of_groups = zip(*(iter(the_list),) * group_size)
Run Code Online (Sandbox Code Playgroud)

例:

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

如果元素的数量不能被N整除,但你仍然希望包含它们,你可以使用izip_longest,但它只能从python 2.6开始使用

izip_longest(*(iter(range(10)),) * 3)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

列表理解和按方法分组显然比zip,izip_longest和map慢

  • 你的Python技能让我有点害怕.:) 好答案! (9认同)
  • 一些解释:`(iter(the_list),)` 是一个元组。`(iter(the_list),)*3` 是一个元组,具有对同一迭代器的三个引用。我们使用带星号的表达式“*(...)”将对迭代器的三个引用作为参数传递给“zip”。`zip` 基本上对给定的参数进行矩阵转置。 (4认同)
  • 这应该是公认的答案.虽然我记得他们是'正确'的方式,但我记不起地图和拉链成语了,这出现在网络搜索中 - 谢谢. (3认同)
  • 这是通过将多个引用传递给同一个迭代器来实现的.这是一个疯狂的黑客. (3认同)
  • 所有解决方案的基准+1。这是人们在 Python 答案中经常忽略的一点,正如人们所看到的,使用哪种方法会产生非常非常大的差异。 (2认同)

Ste*_*teg 47

怎么样:

>>> 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]]
Run Code Online (Sandbox Code Playgroud)

  • 好一个.对于我的用例,首选这种方式比zip更好. (3认同)
  • 这可能不是最快的方法,但肯定是最pythonic的。 (3认同)
  • 干净利落。 (2认同)

Ste*_*202 41

的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)
Run Code Online (Sandbox Code Playgroud)

用法示例:

>>> 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)]
Run Code Online (Sandbox Code Playgroud)

如果你想让最后一个组比其他组更短而不是填充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]]
Run Code Online (Sandbox Code Playgroud)

  • 如果实际列表在中间某处有"无",则最后一个函数也将删除它们. (3认同)

u0b*_*6ae 13

Itertools.groupby是一个很好的工具,这里只是通过使用整数除法来分割整数列表:

>>> 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]
Run Code Online (Sandbox Code Playgroud)

(列表必须从0开始才能以完整的组开头.)

  • 您的代码 _only_ 适用于 `range(i)`,其中 i 是某个整数,而不适用于指定的“任意长度列表”。你的回答不正确。 (2认同)

ken*_*der 9

n = 25    
list_of_lists = [L[i:i+n] for i in range(0, len(L), n)]
Run Code Online (Sandbox Code Playgroud)

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

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