Comment diviser une liste en morceaux de taille égale ?

J'ai une liste de longueur arbitraire, et j'ai besoin de la diviser en morceaux de taille égale pour y effectuer des opérations. Il y a des façons évidentes de le faire, comme garder un compteur et deux listes, et lorsque la deuxième liste se remplit, l'ajouter à la première liste et vider la deuxième liste pour la prochaine série de données, mais cela est potentiellement extrêmement coûteux.

Je me demandais si quelqu'un avait une bonne solution pour des listes de n'importe quelle longueur, par exemple en utilisant des générateurs.

J'ai cherché quelque chose d'utile dans itertools mais je n'ai rien trouvé d'évident. J'ai peut-être raté quelque chose, cependant.

Question connexe : [Quelle est la manière la plus "pythonique" d'itérer sur une liste par morceaux ?][1]

[1] : https://stackoverflow.com/questions/434287/what-is-the-most-pythonic-way-to-iterate-over-a-list-in-chunks

Solution

Voici un générateur qui produit les morceaux que vous voulez :

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 vous utilisez Python 2, vous devez utiliser xrange() au lieu 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]

Vous pouvez aussi simplement utiliser la compréhension de liste au lieu d'écrire une fonction, bien que ce soit une bonne idée d'encapsuler les opérations de ce type dans des fonctions nommées pour que votre code soit plus facile à comprendre. Python 3 :

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

Version Python 2 :

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

Voici un générateur qui fonctionne sur des itérables arbitraires :

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

Exemple :

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

Si vous connaissez la taille de la liste :

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

Si vous ne le savez pas (un itérateur) :

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

Dans ce dernier cas, on peut le reformuler d'une manière plus belle si on peut être sûr que la séquence contient toujours un nombre entier de morceaux de taille donnée (c'est-à-dire qu'il n'y a pas de dernier morceau incomplet).

Commentaires (1)