Como se divide uma lista em pedaços de tamanho uniforme?

Tenho uma lista de comprimento arbitrário, e preciso dividi-la em pedaços de igual tamanho e operar sobre ela. Há algumas maneiras óbvias de fazer isso, como manter um contador e duas listas, e quando a segunda lista se encher, adicioná-la à primeira lista e esvaziar a segunda lista para a próxima rodada de dados, mas isso é potencialmente extremamente caro.

Estava a pensar se alguém tinha uma boa solução para listas de qualquer comprimento, por exemplo, usando geradores.

Eu estava procurando por algo útil em itertools mas eu não consegui'não encontrei nada obviamente útil. Mas talvez'não encontrei nada.

Pergunta relacionada: Qual é a forma mais "pitônica" de iterar sobre uma lista em pedaços?]1

Solução

Aqui está um gerador que rende os pedaços que você quer:

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]

import pprint
pprint.pprint(list(chunks(range(10, 75), 10)))
[[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],
 [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
 [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
 [70, 71, 72, 73, 74]]

Se você estiver utilizando Python 2, você deve utilizar xrange() ao invés de range():

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in xrange(0, len(l), n):
        yield l[i:i + n]

Você também pode simplesmente usar a compreensão de listas em vez de escrever uma função, embora seja uma boa idéia encapsular operações como esta em funções nomeadas para que seu código seja mais fácil de entender. Python 3:

[l[i:i + n] for i in range(0, len(l), n)]

Versão Python 2:

[l[i:i + n] for i in xrange(0, len(l), n)]
Comentários (10)

Aqui está um gerador que trabalha com iterables arbitrários:

def split_seq(iterable, size):
    it = iter(iterable)
    item = list(itertools.islice(it, size))
    while item:
        yield item
        item = list(itertools.islice(it, size))

Exemplo:

>>> import pprint
>>> pprint.pprint(list(split_seq(xrange(75), 10)))
[[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],
 [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
 [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
 [70, 71, 72, 73, 74]]
Comentários (0)

Se você sabe o tamanho da lista:

def SplitList(mylist, chunk_size):
    return [mylist[offs:offs+chunk_size] for offs in range(0, len(mylist), chunk_size)]

Se você não o fizer (um iterador):

def IterChunks(sequence, chunk_size):
    res = []
    for item in sequence:
        res.append(item)
        if len(res) >= chunk_size:
            yield res
            res = []
    if res:
        yield res  # yield the last, incomplete, portion

Neste último caso, pode ser reformulado de uma forma mais bonita se você puder ter certeza de que a sequência sempre contém um número inteiro de pedaços de determinado tamanho (ou seja, não há nenhum último pedaço incompleto).

Comentários (1)