¿Cómo se divide una lista en trozos de tamaño uniforme?

Tengo una lista de longitud arbitraria, y necesito dividirla en trozos de igual tamaño y operar sobre ella. Hay algunas formas obvias de hacer esto, como mantener un contador y dos listas, y cuando la segunda lista se llena, añadirla a la primera lista y vaciar la segunda lista para la siguiente ronda de datos, pero esto es potencialmente muy caro.

Me preguntaba si alguien tenía una buena solución para esto para listas de cualquier longitud, por ejemplo, usando generadores.

Estaba buscando algo útil en itertools pero no pude encontrar nada obviamente útil. Aunque puede que se me haya pasado.

Pregunta relacionada: ¿Cuál es la forma más "pitónica" de iterar sobre una lista en trozos?

Solución

Aquí hay un generador que produce los trozos que quieres:

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

Si estás usando Python 2, deberías usar xrange() en lugar 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]

También puedes usar simplemente la comprensión de listas en lugar de escribir una función, aunque es una buena idea encapsular operaciones como esta en funciones con nombre para que tu código sea más fácil de entender. Python 3:

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

Versión de Python 2:

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

Aquí hay un generador que funciona con iterables arbitrarios:

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

Ejemplo:

>>> 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]]
Comentarios (0)

Si conoce el tamaño de la lista:

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

Si no lo sabes (un 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

En este último caso, se puede reformular de una manera más bonita si se puede estar seguro de que la secuencia siempre contiene un número entero de trozos de tamaño determinado (es decir, no hay ningún último trozo incompleto).

Comentarios (1)