web-dev-qa-db-fra.com

Quel est le moyen le plus "pythonique" de parcourir une liste en morceaux?

J'ai un script Python qui prend en entrée une liste d'entiers que je dois utiliser avec quatre entiers à la fois. Malheureusement, je n'ai pas le contrôle de l'entrée ou je l'aurais transmise sous forme de liste de tuples à quatre éléments. Actuellement, je le répète de cette façon:

for i in xrange(0, len(ints), 4):
    # dummy op for example code
    foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]

Cela ressemble beaucoup à "C-think", cependant, ce qui me laisse supposer qu'il existe un moyen plus pythonique de gérer cette situation. La liste est supprimée après itération, elle n'a donc pas besoin d'être conservée. Peut-être que quelque chose comme ça serait mieux?

while ints:
    foo += ints[0] * ints[1] + ints[2] * ints[3]
    ints[0:4] = []

Vous n'avez toujours pas l'impression que vous avez raison. : - /

Question associée: Comment divisez-vous une liste en morceaux de taille égale en Python?

384
Ben Blank

Modifié à partir de la section recettes de python itertools docs:

from itertools import izip_longest

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return izip_longest(*args, fillvalue=fillvalue)

Exemple
En pseudo-code pour garder l'exemple concis.

grouper('ABCDEFG', 3, 'x') --> 'ABC' 'DEF' 'Gxx'

Remarque:izip_longest est une nouveauté de Python 2.6. En Python 3, utilisez Zip_longest.

268
Craz
def chunker(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))
# (in python 2 use xrange() instead of range() to avoid allocating a list)

Simple. Facile. Vite. Fonctionne avec n'importe quelle séquence:

text = "I am a very, very helpful text"

for group in chunker(text, 7):
   print repr(group),
# 'I am a ' 'very, v' 'ery hel' 'pful te' 'xt'

print '|'.join(chunker(text, 10))
# I am a ver|y, very he|lpful text

animals = ['cat', 'dog', 'rabbit', 'duck', 'bird', 'cow', 'gnu', 'fish']

for group in chunker(animals, 3):
    print group
# ['cat', 'dog', 'rabbit']
# ['duck', 'bird', 'cow']
# ['gnu', 'fish']
346
nosklo

Je suis fan de 

chunkSize= 4
for i in xrange(0, len(ints), chunkSize):
    chunk = ints[i:i+chunkSize]
    # process chunk of size <= chunkSize
98
S.Lott
import itertools
def chunks(iterable,size):
    it = iter(iterable)
    chunk = Tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = Tuple(itertools.islice(it,size))

# though this will throw ValueError if the length of ints
# isn't a multiple of four:
for x1,x2,x3,x4 in chunks(ints,4):
    foo += x1 + x2 + x3 + x4

for chunk in chunks(ints,4):
    foo += sum(chunk)

Autrement:

import itertools
def chunks2(iterable,size,filler=None):
    it = itertools.chain(iterable,itertools.repeat(filler,size-1))
    chunk = Tuple(itertools.islice(it,size))
    while len(chunk) == size:
        yield chunk
        chunk = Tuple(itertools.islice(it,size))

# x2, x3 and x4 could get the value 0 if the length is not
# a multiple of 4.
for x1,x2,x3,x4 in chunks2(ints,4,0):
    foo += x1 + x2 + x3 + x4
21
Markus Jarderot
from itertools import izip_longest

def chunker(iterable, chunksize, filler):
    return izip_longest(*[iter(iterable)]*chunksize, fillvalue=filler)
11
Pedro Henriques

J'avais besoin d'une solution qui fonctionnerait également avec des ensembles et des générateurs. Je ne pouvais rien proposer de très court et joli, mais c'est assez lisible au moins.

def chunker(seq, size):
    res = []
    for el in seq:
        res.append(el)
        if len(res) == size:
            yield res
            res = []
    if res:
        yield res

Liste:

>>> list(chunker([i for i in range(10)], 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

Ensemble:

>>> list(chunker(set([i for i in range(10)]), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

Générateur:

>>> list(chunker((i for i in range(10)), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
8
bcoughlan

La solution idéale à ce problème fonctionne avec des itérateurs (pas seulement des séquences). Cela devrait aussi être rapide.

Voici la solution fournie par la documentation pour itertools:

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

En utilisant le %timeit d'ipython sur mon livre Mac, je reçois 47,5 us par boucle.

Cependant, cela ne fonctionne vraiment pas pour moi car les résultats sont complétés pour former des groupes de taille égale. Une solution sans rembourrage est légèrement plus compliquée. La solution la plus naïve pourrait être:

def grouper(size, iterable):
    i = iter(iterable)
    while True:
        out = []
        try:
            for _ in range(size):
                out.append(i.next())
        except StopIteration:
            yield out
            break

        yield out

Simple, mais assez lent: 693 us par boucle

La meilleure solution que je puisse trouver utilise islice pour la boucle interne:

def grouper(size, iterable):
    it = iter(iterable)
    while True:
        group = Tuple(itertools.islice(it, None, size))
        if not group:
            break
        yield group

Avec le même jeu de données, je reçois 305 us par boucle.

Impossible d'obtenir une solution pure plus rapidement que cela, je présente la solution suivante avec une mise en garde importante: si vos données d'entrée contiennent des instances de filldata, vous risquez d'obtenir une réponse erronée.

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    for i in itertools.izip_longest(fillvalue=fillvalue, *args):
        if Tuple(i)[-1] == fillvalue:
            yield Tuple(v for v in i if v != fillvalue)
        else:
            yield i

Je n'aime vraiment pas cette réponse, mais c'est beaucoup plus rapide. 124 nous par boucle

7
rhettg

Utiliser map () au lieu de Zip () corrige le problème de remplissage dans la réponse de J.F. Sebastian:

>>> def chunker(iterable, chunksize):
...   return map(None,*[iter(iterable)]*chunksize)

Exemple:

>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9'), ('0', None, None)]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8'), ('9', '0', None, None)]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]
6
catwell

Semblable à d'autres propositions, mais pas exactement identique, j'aime bien le faire, car c'est simple et facile à lire:

it = iter([1, 2, 3, 4, 5, 6, 7, 8, 9])
for chunk in Zip(it, it, it, it):
    print chunk

>>> (1, 2, 3, 4)
>>> (5, 6, 7, 8)

De cette façon, vous n'obtiendrez pas le dernier morceau partiel. Si vous voulez obtenir (9, None, None, None) comme dernier morceau, utilisez simplement izip_longest à partir de itertools.

6
kriss

Puisque personne n'en a encore parlé, voici une solution Zip():

>>> def chunker(iterable, chunksize):
...     return Zip(*[iter(iterable)]*chunksize)

Cela ne fonctionne que si la longueur de votre séquence est toujours divisible par la taille du bloc ou si vous ne vous souciez pas du dernier.

Exemple:

>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9')]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8')]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]

Ou utilisez itertools.izip pour retourner un itérateur au lieu d'une liste:

>>> from itertools import izip
>>> def chunker(iterable, chunksize):
...     return izip(*[iter(iterable)]*chunksize)

Le rembourrage peut être corrigé avec La réponse de @ ΤΖΩΤΖΙΟΥ :

>>> from itertools import chain, izip, repeat
>>> def chunker(iterable, chunksize, fillvalue=None):
...     it   = chain(iterable, repeat(fillvalue, chunksize-1))
...     args = [it] * chunksize
...     return izip(*args)
4
jfs

Si cela ne vous dérange pas d'utiliser un paquet externe, vous pouvez utiliser iteration_utilities.grouper from iteration_utilties 1. Il supporte tous les iterables (pas seulement les séquences):

from iteration_utilities import grouper
seq = list(range(20))
for group in grouper(seq, 4):
    print(group)

qui imprime:

(0, 1, 2, 3)
(4, 5, 6, 7)
(8, 9, 10, 11)
(12, 13, 14, 15)
(16, 17, 18, 19)

Si la longueur n'est pas un multiple de la taille du groupe, elle prend également en charge le remplissage (dernier groupe incomplet) ou la troncature (suppression du dernier groupe incomplet):

from iteration_utilities import grouper
seq = list(range(17))
for group in grouper(seq, 4):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16,)

for group in grouper(seq, 4, fillvalue=None):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16, None, None, None)

for group in grouper(seq, 4, truncate=True):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)

1 Disclaimer: Je suis l'auteur de ce paquet.

4
MSeifert

Utiliser de petites fonctions et des choses ne m'intéresse pas vraiment; Je préfère simplement utiliser des tranches:

data = [...]
chunk_size = 10000 # or whatever
chunks = [data[i:i+chunk_size] for i in xrange(0,len(data),chunk_size)]
for chunk in chunks:
    ...
3
Will

Si la liste est longue, la méthode la plus performante consiste à utiliser un générateur:

def get_chunk(iterable, chunk_size):
    result = []
    for item in iterable:
        result.append(item)
        if len(result) == chunk_size:
            yield Tuple(result)
            result = []
    if len(result) > 0:
        yield Tuple(result)

for x in get_chunk([1,2,3,4,5,6,7,8,9,10], 3):
    print x

(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
(10,)
3
Robert Rossney

Avec NumPy, c'est simple:

ints = array([1, 2, 3, 4, 5, 6, 7, 8])
for int1, int2 in ints.reshape(-1, 2):
    print(int1, int2)

sortie:

1 2
3 4
5 6
7 8
2
endolith

Une autre approche consisterait à utiliser la forme à deux arguments de iter

from itertools import islice

def group(it, size):
    it = iter(it)
    return iter(lambda: Tuple(islice(it, size)), ())

Ceci peut être facilement adapté pour utiliser un rembourrage (similaire à Markus Jarderot ’s (s)):

from itertools import islice, chain, repeat

def group_pad(it, size, pad=None):
    it = chain(iter(it), repeat(pad))
    return iter(lambda: Tuple(islice(it, size)), (pad,) * size)

Ceux-ci peuvent même être combinés pour un remplissage optionnel:

_no_pad = object()
def group(it, size, pad=_no_pad):
    if pad == _no_pad:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(pad))
        sentinel = (pad,) * size
    return iter(lambda: Tuple(islice(it, size)), sentinel)
2
senderle

À moins que quelque chose me manque, la solution simple suivante avec des expressions de générateur n'a pas été mentionnée. Il suppose que la taille et le nombre de morceaux sont connus (ce qui est souvent le cas), et qu'aucun remplissage n'est requis:

def chunks(it, n, m):
    """Make an iterator over m first chunks of size n.
    """
    it = iter(it)
    # Chunks are presented as tuples.
    return (Tuple(next(it) for _ in range(n)) for _ in range(m))
2
Alexey

Voici un chunker sans importations prenant en charge les générateurs:

def chunks(seq, size):
    it = iter(seq)
    while True:
        ret = Tuple(it.next() for _ in range(size))
        if len(ret) == size:
            yield ret
        else:
            raise StopIteration()

Exemple d'utilisation:

>>> def foo():
...     i = 0
...     while True:
...         i += 1
...         yield i
...
>>> c = chunks(foo(), 3)
>>> c.next()
(1, 2, 3)
>>> c.next()
(4, 5, 6)
>>> list(chunks('abcdefg', 2))
[('a', 'b'), ('c', 'd'), ('e', 'f')]
1
Cuadue
def chunker(iterable, n):
    """Yield iterable in chunk sizes.

    >>> chunks = chunker('ABCDEF', n=4)
    >>> chunks.next()
    ['A', 'B', 'C', 'D']
    >>> chunks.next()
    ['E', 'F']
    """
    it = iter(iterable)
    while True:
        chunk = []
        for i in range(n):
            try:
                chunk.append(it.next())
            except StopIteration:
                yield chunk
                raise StopIteration
        yield chunk

if __== '__main__':
    import doctest

    doctest.testmod()
1
ksindi

A propos de la solution donnée par J.F. Sebastianici :

def chunker(iterable, chunksize):
    return Zip(*[iter(iterable)]*chunksize)

C'est intelligent, mais a un inconvénient: retournez toujours Tuple. Comment obtenir de la ficelle à la place?
Bien sûr, vous pouvez écrire ''.join(chunker(...)), mais le Tuple temporaire est quand même construit.

Vous pouvez vous débarrasser du tuple temporaire en écrivant Zip, comme ceci:

class IteratorExhausted(Exception):
    pass

def translate_StopIteration(iterable, to=IteratorExhausted):
    for i in iterable:
        yield i
    raise to # StopIteration would get ignored because this is generator,
             # but custom exception can leave the generator.

def custom_Zip(*iterables, reductor=Tuple):
    iterators = Tuple(map(translate_StopIteration, iterables))
    while True:
        try:
            yield reductor(next(i) for i in iterators)
        except IteratorExhausted: # when any of iterators get exhausted.
            break

Ensuite

def chunker(data, size, reductor=Tuple):
    return custom_Zip(*[iter(data)]*size, reductor=reductor)

Exemple d'utilisation:

>>> for i in chunker('12345', 2):
...     print(repr(i))
...
('1', '2')
('3', '4')
>>> for i in chunker('12345', 2, ''.join):
...     print(repr(i))
...
'12'
'34'
1
GingerPlusPlus

Je ne veux jamais que mes morceaux soient rembourrés, cette exigence est donc essentielle. Je trouve que la capacité de travailler sur n'importe quelle itération est également une exigence. Compte tenu de cela, j'ai décidé d'étendre la réponse acceptée, https://stackoverflow.com/a/434411/1074659

Les performances pèsent légèrement sur cette approche si le remplissage n’est pas souhaité en raison de la nécessité de comparer et de filtrer les valeurs remplies. Cependant, pour les gros morceaux, cet utilitaire est très performant.

#!/usr/bin/env python3
from itertools import Zip_longest


_UNDEFINED = object()


def chunker(iterable, chunksize, fillvalue=_UNDEFINED):
    """
    Collect data into chunks and optionally pad it.

    Performance worsens as `chunksize` approaches 1.

    Inspired by:
        https://docs.python.org/3/library/itertools.html#itertools-recipes

    """
    args = [iter(iterable)] * chunksize
    chunks = Zip_longest(*args, fillvalue=fillvalue)
    yield from (
        filter(lambda val: val is not _UNDEFINED, chunk)
        if chunk[-1] is _UNDEFINED
        else chunk
        for chunk in chunks
    ) if fillvalue is _UNDEFINED else chunks
1
frankish
def group_by(iterable, size):
    """Group an iterable into lists that don't exceed the size given.

    >>> group_by([1,2,3,4,5], 2)
    [[1, 2], [3, 4], [5]]

    """
    sublist = []

    for index, item in enumerate(iterable):
        if index > 0 and index % size == 0:
            yield sublist
            sublist = []

        sublist.append(item)

    if sublist:
        yield sublist
1
Wilfred Hughes

Dans votre deuxième méthode, je passerais au groupe de 4 suivant en procédant ainsi:

ints = ints[4:]

Cependant, comme je n’ai fait aucune mesure de la performance, je ne sais pas laquelle pourrait être plus efficace.

Cela dit, je choisirais généralement la première méthode. Ce n'est pas beau, mais c'est souvent une conséquence de l'interface avec le monde extérieur.

1
Greg Hewgill

Vous pouvez utiliser partition ou chunks function from funcy library:

from funcy import partition

for a, b, c, d in partition(4, ints):
    foo += a * b * c * d

Ces fonctions ont également les versions d'itérateur ipartition et ichunks, qui seront plus efficaces dans ce cas.

Vous pouvez également jeter un œil à leur mise en œuvre .

1
Suor

J'aime cette approche. Il se sent simple et pas magique, supporte tous les types itérables et ne nécessite pas d’importation.

def chunk_iter(iterable, chunk_size):
it = iter(iterable)
while True:
    chunk = Tuple(next(it) for _ in range(chunk_size))
    if not chunk:
        break
    yield chunk
1
BallpointBen

Encore une autre réponse, dont les avantages sont:

1) facilement compréhensible
2) Fonctionne sur n’importe quelle séquence, pas seulement des séquences (certaines des réponses ci-dessus vont s’étouffer avec les poignées de fichiers)
3) Ne charge pas le bloc en mémoire en une fois
4) Ne fait pas une longue liste de références au même itérateur en mémoire
5) Pas de remplissage des valeurs de remplissage à la fin de la liste

Cela étant dit, je n'ai pas chronométré le temps, ce qui pourrait être plus lent que certaines des méthodes les plus intelligentes, et certains des avantages pourraient ne pas être pertinents compte tenu du cas d'utilisation.

def chunkiter(iterable, size):
  def inneriter(first, iterator, size):
    yield first
    for _ in xrange(size - 1): 
      yield iterator.next()
  it = iter(iterable)
  while True:
    yield inneriter(it.next(), it, size)

In [2]: i = chunkiter('abcdefgh', 3)
In [3]: for ii in i:                                                
          for c in ii:
            print c,
          print ''
        ...:     
        a b c 
        d e f 
        g h 

Mettre à jour:
Quelques inconvénients dus au fait que les boucles interne et externe extraient des valeurs du même itérateur:
1) continue ne fonctionne pas comme prévu dans la boucle externe - il continue simplement à l'élément suivant au lieu de sauter un bloc. Cependant, cela ne semble pas être un problème car il n’ya rien à tester dans la boucle externe.
2) break ne fonctionne pas comme prévu dans la boucle interne - le contrôle se retrouvera dans la boucle interne avec le prochain élément de l'itérateur. Pour ignorer des morceaux entiers, enveloppez l’itérateur interne (ii ci-dessus) dans un tuple, par ex. for c in Tuple(ii), ou définir un indicateur et épuiser l'itérateur.

1
elhefe

Pour éviter toutes les conversions vers une liste import itertools et:

>>> for k, g in itertools.groupby(xrange(35), lambda x: x/10):
...     list(g)

Produit:

... 
0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
2 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
3 [30, 31, 32, 33, 34]
>>> 

J'ai vérifié groupby et il ne convertit pas en liste ni n'utilise len donc je pense que cela retardera la résolution de chaque valeur jusqu'à ce qu'elle soit réellement utilisée. Malheureusement, aucune des réponses disponibles (pour le moment) ne semblait offrir cette variation.

Évidemment, si vous devez manipuler chaque élément à son tour, imbriquez une boucle for sur g:

for k,g in itertools.groupby(xrange(35), lambda x: x/10):
    for i in g:
       # do what you need to do with individual items
    # now do what you need to do with the whole group

Mon intérêt spécifique à cet égard était la nécessité de recourir à un générateur pour soumettre des modifications allant jusqu'à 1 000 lots à l'API gmail:

    messages = a_generator_which_would_not_be_smart_as_a_list
    for idx, batch in groupby(messages, lambda x: x/1000):
        batch_request = BatchHttpRequest()
        for message in batch:
            batch_request.add(self.service.users().messages().modify(userId='me', id=message['id'], body=msg_labels))
        http = httplib2.Http()
        self.credentials.authorize(http)
        batch_request.execute(http=http)
1
John Mee

Tout à fait Pythonic ici (vous pouvez également insérer le corps de la fonction split_groups)

import itertools
def split_groups(iter_in, group_size):
    return ((x for _, x in item) for _, item in itertools.groupby(enumerate(iter_in), key=lambda x: x[0] // group_size))

for x, y, z, w in split_groups(range(16), 4):
    foo += x * y + z * w
0
Andrey Cizov

Solution ponctuelle, unipolaire, permettant de parcourir une liste x en morceaux de taille 4 -

for a, b, c, d in Zip(x[0::4], x[1::4], x[2::4], x[3::4]):
    ... do something with a, b, c and d ...
0
Tutul

Au début, je l’ai conçu pour scinder les chaînes en sous-chaînes afin d’analyser les chaînes contenant des éléments hex.
Aujourd'hui, je l'ai transformé en générateur complexe, mais toujours simple.

def chunker(iterable, size, reductor, condition):
    it = iter(iterable)
    def chunk_generator():
        return (next(it) for _ in range(size))
    chunk = reductor(chunk_generator())
    while condition(chunk):
        yield chunk
        chunk = reductor(chunk_generator())

Arguments:

Les évidents

  • iterable est un itératif/itérateur/générateur qui contient/génère/itère des données d’entrée,
  • size est, bien sûr, la taille du bloc que vous voulez obtenir,

Plus intéressant

  • reductor est un appelable, qui reçoit le générateur itérant sur le contenu du bloc.
    .__ Je m'attendrais à ce qu'il retourne une séquence ou une chaîne, mais je ne l'exige pas.

    Vous pouvez transmettre cet argument par exemple list, Tuple, set, frozenset,
    ou n'importe quoi d'amateur. Je passerais cette fonction en retournant une chaîne
    (à condition que iterable contienne/génère/itère sur des chaînes):

    def concatenate(iterable):
        return ''.join(iterable)
    

    Notez que reductor peut provoquer la fermeture du générateur en soulevant une exception.

  • condition est un appelable qui reçoit tout ce que reductor a renvoyé.
    Il décide de l’approuver et de le céder (en retournant tout ce qui évalue à True),
    .__ ou de le refuser et de terminer le travail du générateur (en renvoyant quelque chose d'autre ou en levant une exception).

    Lorsque le nombre d'éléments dans iterable n'est pas divisible par size, lorsque it est épuisé, reductor recevra un générateur générant moins d'éléments que size.
    Appelons ces éléments lasts elements.

    J'ai invité deux fonctions à passer comme argument: 

    • lambda x:x - les éléments lasts seront cédés.

    • lambda x: len(x)==<size> - les éléments lasts seront rejetés.
      remplacer <size> en utilisant un nombre égal à size

0
GingerPlusPlus

Cette réponse divise une liste de chaînes , f.ex. pour atteindre la conformité de longueur de ligne PEP8:

def split(what, target_length=79):
    '''splits list of strings into sublists, each 
    having string length at most 79'''
    out = [[]]
    while what:
        if len("', '".join(out[-1])) + len(what[0]) < target_length:
            out[-1].append(what.pop(0))
        else:
            if not out[-1]: # string longer than target_length
                out[-1] = [what.pop(0)]
            out.append([])
    return out

Utilisé comme

>>> split(['deferred_income', 'long_term_incentive', 'restricted_stock_deferred', 'shared_receipt_with_poi', 'loan_advances', 'from_messages', 'other', 'director_fees', 'bonus', 'total_stock_value', 'from_poi_to_this_person', 'from_this_person_to_poi', 'restricted_stock', 'salary', 'total_payments', 'exercised_stock_options'], 75)
[['deferred_income', 'long_term_incentive', 'restricted_stock_deferred'], ['shared_receipt_with_poi', 'loan_advances', 'from_messages', 'other'], ['director_fees', 'bonus', 'total_stock_value', 'from_poi_to_this_person'], ['from_this_person_to_poi', 'restricted_stock', 'salary', 'total_payments'], ['exercised_stock_options']]
0
serv-inc

Il ne semble pas y avoir de jolie façon de faire cela. Ici est une page comportant un certain nombre de méthodes, notamment:

def split_seq(seq, size):
    newseq = []
    splitsize = 1.0/size*len(seq)
    for i in range(size):
        newseq.append(seq[int(round(i*splitsize)):int(round((i+1)*splitsize))])
    return newseq
0
Harley Holcombe

J'espère qu'en transformant un itérateur d'une liste, je ne copie pas simplement une partie de la liste. Les générateurs peuvent être découpés et ils seront toujours automatiquement générateurs, tandis que les listes seront découpées en énormes morceaux de 1000 entrées, ce qui est moins efficace. 

def iter_group(iterable, batch_size:int):
    length = len(iterable)
    start = batch_size*-1
    end = 0
    while(end < length):
        start += batch_size
        end += batch_size
        if type(iterable) == list:
            yield (iterable[i] for i in range(start,min(length-1,end)))
        else:
            yield iterable[start:end]

Usage:

items = list(range(1,1251))

for item_group in iter_group(items, 1000):
    for item in item_group:
        print(item)
0
Ben

Si les listes ont la même taille, vous pouvez les combiner en listes de 4-tuples avec Zip(). Par exemple:

# Four lists of four elements each.

l1 = range(0, 4)
l2 = range(4, 8)
l3 = range(8, 12)
l4 = range(12, 16)

for i1, i2, i3, i4 in Zip(l1, l2, l3, l4):
    ...

Voici ce que la fonction Zip() produit:

>>> print l1
[0, 1, 2, 3]
>>> print l2
[4, 5, 6, 7]
>>> print l3
[8, 9, 10, 11]
>>> print l4
[12, 13, 14, 15]
>>> print Zip(l1, l2, l3, l4)
[(0, 4, 8, 12), (1, 5, 9, 13), (2, 6, 10, 14), (3, 7, 11, 15)]

Si les listes sont volumineuses et que vous ne souhaitez pas les combiner en une liste plus grande, utilisez itertools.izip(), qui produit un itérateur plutôt qu'une liste.

from itertools import izip

for i1, i2, i3, i4 in izip(l1, l2, l3, l4):
    ...
0
Brian Clapper

Il est facile de faire fonctionner itertools.groupby pour que vous obteniez un itérable d'iterables, sans créer de listes temporaires:

groupby(iterable, (lambda x,y: (lambda z: x.next()/y))(count(),100))

Ne soyez pas rebutés par les lambdas imbriqués, lambda externe ne s'exécute qu'une seule fois pour mettre le générateur count() et la constante 100 dans la portée du lambda interne.

J'utilise cela pour envoyer des morceaux de lignes à mysql.

for k,v in groupby(bigdata, (lambda x,y: (lambda z: x.next()/y))(count(),100))):
    cursor.executemany(sql, v)
0
topkara

Pourquoi ne pas utiliser la compréhension de liste

l = [1 , 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
n = 4
filler = 0
fills = len(l) % n
chunks = ((l + [filler] * fills)[x * n:x * n + n] for x in range(int((len(l) + n - 1)/n)))
print(chunks)

[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 0]]
0
Mohammad Azim