Hvordan deler du opp en liste i like store biter?

Jeg har en liste av vilkårlig lengde, og jeg må dele den opp i like store biter og operere på den. Det er noen åpenbare måter å gjøre dette på, som å ha en teller og to lister, og når den andre listen fylles opp, legge den til den første listen og tømme den andre listen for neste runde med data, men dette er potensielt ekstremt dyrt.

Jeg lurte på om noen hadde en god løsning på dette for lister av hvilken som helst lengde, f.eks. ved hjelp av generatorer.

Jeg lette etter noe nyttig i itertools, men jeg kunne ikke finne noe åpenbart nyttig. Kan ha gått glipp av det, skjønt.

Relatert spørsmål: Hva er den mest "pytoniske" måten å iterere over en liste i biter?

Løsning

Her er en generator som gir de bitene du vil ha:

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

Hvis du bruker Python 2, bør du bruke xrange() i stedet for 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]

Du kan også bare bruke listeforståelse i stedet for å skrive en funksjon, selv om det er en god idé å innkapsle operasjoner som dette i navngitte funksjoner slik at koden din er lettere å forstå. Python 3:

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

Python 2 versjon:

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

Her er en generator som fungerer på vilkårlige iterables:

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

Eksempel:

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

Hvis du kjenner listestørrelsen:

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

Hvis du ikke gjør det (en iterator):

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

I sistnevnte tilfelle kan det omformuleres på en vakrere måte hvis du kan være sikker på at sekvensen alltid inneholder et helt antall biter av gitt størrelse (dvs. at det ikke er noen ufullstendig siste bit).

Kommentarer (1)