Come si fa a dividere una lista in pezzi di dimensioni uguali?

Ho una lista di lunghezza arbitraria, e ho bisogno di dividerla in pezzi di uguale dimensione e operare su di essa. Ci sono alcuni modi ovvi per farlo, come tenere un contatore e due liste, e quando la seconda lista si riempie, aggiungerla alla prima lista e svuotare la seconda lista per il prossimo giro di dati, ma questo è potenzialmente estremamente costoso.

Mi chiedevo se qualcuno avesse una buona soluzione per liste di qualsiasi lunghezza, ad esempio usando dei generatori.

Stavo cercando qualcosa di utile in itertools, ma non ho trovato nulla di ovviamente utile. Potrebbe essermi sfuggito, però.

Domanda correlata: Qual è il modo più "pitonico" per iterare su una lista a blocchi?

Soluzione

Ecco un generatore che produce i pezzi che vuoi:

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 state usando Python 2, dovreste usare xrange() invece di 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]

Inoltre potete semplicemente usare la comprensione della lista invece di scrivere una funzione, anche se è una buona idea incapsulare operazioni come questa in funzioni con nome in modo che il vostro codice sia più facile da capire. Python 3:

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

Versione Python 2:

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

Ecco un generatore che lavora su iterabili arbitrari:

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

Esempio:

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

Se conosci le dimensioni della lista:

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

Se non lo sai (un iteratore):

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

In quest'ultimo caso, può essere riformulato in un modo più bello se si può essere sicuri che la sequenza contiene sempre un numero intero di pezzi di una data dimensione (cioè non c'è un ultimo pezzo incompleto).

Commentari (1)