Ako rozdeliť zoznam na rovnomerne veľké časti?

Mám zoznam ľubovoľnej dĺžky a potrebujem ho rozdeliť na rovnako veľké časti a pracovať s ním. Existuje niekoľko zrejmých spôsobov, ako to urobiť, napríklad viesť počítadlo a dva zoznamy, a keď sa druhý zoznam naplní, pridať ho do prvého zoznamu a vyprázdniť druhý zoznam pre ďalšie kolo údajov, ale to je potenciálne veľmi nákladné.

Zaujímalo ma, či má niekto dobré riešenie pre zoznamy ľubovoľnej dĺžky, napr. pomocou generátorov.

Hľadal som niečo užitočné v itertools, ale nič zjavne užitočné som nenašiel. Možno som to však prehliadol.

Súvisiaca otázka: Aký je najviac "pythonický" spôsob iterácie nad zoznamom po častiach?

Riešenie

Tu je generátor, ktorý vám poskytne požadované časti:

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

Ak používate Python 2, mali by ste použiť xrange() namiesto 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]

Namiesto písania funkcie môžete tiež jednoducho použiť chápanie zoznamu, hoci je dobré zapuzdriť takéto operácie do pomenovaných funkcií, aby bol váš kód zrozumiteľnejší. Python 3:

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

Verzia Python 2:

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

Tu je generátor, ktorý pracuje s ľubovoľnými iterovateľnými tabuľkami:

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

Príklad:

>>> 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]]
Komentáre (0)

Ak poznáte veľkosť zoznamu:

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

Ak nie (iterátor):

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

V druhom prípade sa to dá preformulovať krajším spôsobom, ak si môžete byť istí, že postupnosť obsahuje vždy celý počet kusov danej veľkosti (t. j. neexistuje neúplný posledný kus).

Komentáre (1)