如何将任意长度的列表拆分为大小相等的块?


请参阅如果数据结果将直接用于循环,并且不需要存储,则如何以块形式遍历列表。

对于字符串输入的同一问题,请参见每n个字符拆分字符串?。相同的技术通常适用,但也有一些变化。


当前回答

在这一点上,我认为我们需要一个递归生成器,以防万一。。。

在python 2中:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e

在python 3中:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    yield from chunks(li[n:], n)

此外,在大规模外星人入侵的情况下,装饰递归生成器可能会变得很方便:

def dec(gen):
    def new_gen(li, n):
        for e in gen(li, n):
            if e == []:
                return
            yield e
    return new_gen

@dec
def chunks(li, n):
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e

其他回答

假设列表是第一个

import math

# length of the list len(lst) is ln
# size of a chunk is size

for num in range ( math.ceil(ln/size) ):
    start, end = num*size, min((num+1)*size, ln)
    print(lst[start:end])

您可以使用更多的intertools.chunked_甚至与math.eil一起使用。这可能是最容易理解的吗?

from math import ceil
import more_itertools as mit
from pprint import pprint

pprint([*mit.chunked_even(range(19), ceil(19 / 5))])
# [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15], [16, 17, 18]]

pprint([*mit.chunked_even(range(20), ceil(20 / 5))])
# [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15], [16, 17, 18, 19]]

pprint([*mit.chunked_even(range(21), ceil(21 / 5))])
# [[0, 1, 2, 3, 4],
# [5, 6, 7, 8],
# [9, 10, 11, 12],
# [13, 14, 15, 16],
# [17, 18, 19, 20]]

pprint([*mit.chunked_even(range(3), ceil(3 / 5))])
# [[0], [1], [2]]


这适用于v2/v3,可内联,基于生成器,仅使用标准库:

import itertools
def split_groups(iter_in, group_size):
    return ((x for _, x in item) for _, item in itertools.groupby(enumerate(iter_in), key=lambda x: x[0] // group_size))

这项任务可以在公认答案中使用生成器轻松完成。我正在添加实现长度方法的类实现,这可能对某些人有用。我需要知道进度(使用tqdm),所以生成器应该返回块的数量。

class ChunksIterator(object):
    def __init__(self, data, n):
        self._data = data
        self._l = len(data)
        self._n = n

    def __iter__(self):
        for i in range(0, self._l, self._n):
            yield self._data[i:i + self._n]

    def __len__(self):
        rem = 1 if self._l % self._n != 0 else 0
        return self._l // self._n + rem

用法:

it = ChunksIterator([1,2,3,4,5,6,7,8,9], 2)
print(len(it))
for i in it:
  print(i)

因为这里的每个人都在谈论迭代器。boltons有一个完美的方法,叫做iterutils.chunked_iter。

from boltons import iterutils

list(iterutils.chunked_iter(list(range(50)), 11))

输出:

[[0, 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, 36, 37, 38, 39, 40, 41, 42, 43],
 [44, 45, 46, 47, 48, 49]]

但如果您不想在内存上手下留情,您可以使用旧方法,首先使用iterutils.chunked存储完整列表。