Une mise en œuvre succincte est :
chunker = lambda iterable, n: (ifilterfalse(lambda x: x == (), chunk) for chunk in (izip_longest(*[iter(iterable)]*n, fillvalue=())))
Cela fonctionne parce que [iter(iterable)]*n
est une liste contenant le même itérateur n fois ; le fait de passer dessus prend un élément de chaque itérateur dans la liste, qui est le même itérateur avec pour résultat que chaque élément de zip contient un groupe de n
articles.
izip_longest
est nécessaire pour consommer entièrement l'itérable sous-jacent, plutôt que d'arrêter l'itération lorsque le premier itérateur épuisé est atteint, ce qui supprime tout reste de l'itérable. iterable
. Il est donc nécessaire de filtrer la valeur de remplissage. Une implémentation un peu plus robuste serait donc :
def chunker(iterable, n):
class Filler(object): pass
return (ifilterfalse(lambda x: x is Filler, chunk) for chunk in (izip_longest(*[iter(iterable)]*n, fillvalue=Filler)))
Cela garantit que la valeur de remplissage n'est jamais un élément dans l'itérable sous-jacent. En utilisant la définition ci-dessus :
iterable = range(1,11)
map(tuple,chunker(iterable, 3))
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10,)]
map(tuple,chunker(iterable, 2))
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
map(tuple,chunker(iterable, 4))
[(1, 2, 3, 4), (5, 6, 7, 8), (9, 10)]
Cette implémentation fait presque ce que vous voulez, mais elle a des problèmes :
def chunks(it, step):
start = 0
while True:
end = start+step
yield islice(it, start, end)
start = end
(La différence est que, parce que islice
ne lève pas StopIteration ou quoi que ce soit d'autre sur les appels qui vont au-delà de la fin de l'option it
ce rendement sera éternel ; il y a aussi la question un peu délicate que la islice
les résultats doivent être consommés avant que ce générateur soit itéré).
Pour générer la fenêtre mobile de manière fonctionnelle :
izip(count(0, step), count(step, step))
Donc ça devient :
(it[start:end] for (start,end) in izip(count(0, step), count(step, step)))
Mais, cela crée toujours un itérateur infini. Donc, vous avez besoin de takewhile (ou peut-être quelque chose d'autre serait mieux) pour le limiter :
chunk = lambda it, step: takewhile((lambda x: len(x) > 0), (it[start:end] for (start,end) in izip(count(0, step), count(step, step))))
g = chunk(range(1,11), 3)
tuple(g)
([1, 2, 3], [4, 5, 6], [7, 8, 9], [10])
3 votes
@kindall : C'est proche, mais pas identique, en raison de la gestion du dernier morceau.
5 votes
C'est légèrement différent, car cette question concernait les listes, et celle-ci est plus générale, les itérateurs. Bien que la réponse semble être la même au final.
0 votes
@recursive : Oui, après avoir lu le fil de discussion lié complètement, j'ai trouvé que tout dans ma réponse apparaît déjà quelque part dans l'autre fil de discussion.
0 votes
stackoverflow.com/a/312464/3798964