3115 Stimmen

Wie kann ich eine Liste in gleich große Stücke aufteilen?

Wie kann ich eine Liste beliebiger Länge in gleich große Stücke aufteilen?


Siehe auch: <a href="https://stackoverflow.com/q/434287">Wie man über eine Liste in Stücken iteriert</a>.<br>Um Zeichenfolgen zu segmentieren, siehe <a href="https://stackoverflow.com/questions/9475241">Zeichenfolge alle n Zeichen aufteilen?</a>.

40 Stimmen

Bevor Sie eine neue Antwort veröffentlichen, beachten Sie bitte, dass es bereits mehr als 60 Antworten auf diese Frage gibt. Stellen Sie sicher, dass Ihre Antwort Informationen enthält, die nicht unter den vorhandenen Antworten sind.

3 Stimmen

Die Zeichenfolgenäquivalente dieser Frage: String alle n Zeichen teilen? (während einige Antworten sich überschneiden und für beide gelten, gibt es einige, die eindeutig sind).

4424voto

Ned Batchelder Punkte 342778

Hier ist ein Generator, der gleich große Abschnitte generiert:

def chunks(lst, n):
    """Gibt aufeinanderfolgende Abschnitte der Größe n von lst zurück."""
    for i in range(0, len(lst), n):
        yield lst[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]]

Für Python 2, Verwendung von xrange anstelle von range:

def chunks(lst, n):
    """Gibt aufeinanderfolgende Abschnitte der Größe n von lst zurück."""
    for i in xrange(0, len(lst), n):
        yield lst[i:i + n]

Im Folgenden finden Sie einen List Comprehension One-Liner. Die oben genannte Methode ist jedoch vorzuziehen, da die Verwendung benannter Funktionen den Code leichter verständlich macht. Für Python 3:

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

Für Python 2:

[lst[i:i + n] for i in xrange(0, len(lst), n)]

92 Stimmen

Was passiert, wenn wir die Länge der Liste nicht angeben können? Probieren Sie es mit itertools.repeat([ 1, 2, 3 ]), z.B.

56 Stimmen

Das ist eine interessante Erweiterung der Frage, aber die Ursprüngliche Frage hat eindeutig nach der Bearbeitung einer Liste gefragt.

2 Stimmen

@jespern Ich nehme an, dass Sie bei einer unendlichen oder unendlichen Liste zur verwandten Frage gehen, die J.F. Sebastian verlinkt hat: Was ist der "pythonischste" Weg, um über eine Liste in Abschnitten zu iterieren?

661voto

oremj Punkte 6456

Etwas super Einfaches:

def chunks(xs, n):
    n = max(1, n)
    return (xs[i:i+n] for i in range(0, len(xs), n))

Für Python 2, verwenden Sie xrange() anstelle von range().

7 Stimmen

Oder (wenn wir unterschiedliche Darstellungen dieser bestimmten Funktion machen): Sie könnten eine Lambda-Funktion über folgenden Code definieren: lambda x, y: [x[i:i+y] for i in range(0, len(x), y)] . Ich liebe diese List-Comprehension-Methode!

0 Stimmen

Unter Verwendung von Kurzschlusstaktiken, len(l) or 1, um mit leeren Listen umzugehen.

0 Stimmen

# geben Generator von Chunks der Größe n von der Liste l zurück

435voto

Moj Punkte 5415

Ich weiß, dass dies etwas alt ist, aber bisher hat noch niemand numpy.array_split erwähnt:

import numpy as np

lst = range(50)
np.array_split(lst, 5)

Ergebnis:

[array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
 array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),
 array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29]),
 array([30, 31, 32, 33, 34, 35, 36, 37, 38, 39]),
 array([40, 41, 42, 43, 44, 45, 46, 47, 48, 49])]

78 Stimmen

Dies ermöglicht es Ihnen, die Gesamtanzahl der Chunks festzulegen, nicht die Anzahl der Elemente pro Chunk.

4 Stimmen

Es ist nicht schwer, x zu lösen...: np.array_split(lst, int(len(lst)/5)), um eine Liste zu erhalten, in der die Länge jeder Unterliste 5 oder weniger beträgt.

0 Stimmen

Verwendung der Methode von @PéterSzabó-tóth führt zu einem Fehler, wenn Ihre Zahl unter 0 fällt, und das ist schlecht.

360voto

tzot Punkte 86792

Direkt aus der (alten) Python-Dokumentation (Rezepte für itertools):

from itertools import izip, chain, repeat

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)

Die aktuelle Version, wie von J.F.Sebastian vorgeschlagen:

#from itertools import izip_longest as zip_longest # für Python 2.x
from itertools import zip_longest # für Python 3.x
#from six.moves import zip_longest # für beide (verwendet die six-Kompatibilitätsbibliothek)

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)

Ich nehme an, Guidos Zeitmaschine funktioniert — funktionierte — wird funktionieren — wird funktioniert haben — funktionierte wieder.

Diese Lösungen funktionieren, weil [iter(iterable)]*n (oder das Äquivalent in der früheren Version) einen ein Iterator erzeugt, der n Mal in der Liste wiederholt wird. izip_longest führt dann effektiv eine Rundum-Robin von "jedem" Iterator durch; da dies derselbe Iterator ist, wird er bei jedem Aufruf vorangebracht, was dazu führt, dass jedes solche Zip-Roundrobin ein Tupel von n Elementen erzeugt.

Python 3.12

itertools.batched ist verfügbar.

23 Stimmen

Upvoted this because it works on generators (no len) and uses the generally faster itertools module.

119 Stimmen

Ein klassisches Beispiel für die schicke itertools funktionale Herangehensweise, die sich als unlesbarer Murks herausstellt, im Vergleich zu einer einfachen und naiven reinen Python-Implementierung.

19 Stimmen

@wim Angesichts dessen, dass diese Antwort als ein Ausschnitt aus der Python-Dokumentation begann, würde ich vorschlagen, dass Sie ein Problem auf bugs.python.org öffnen.

307voto

senderle Punkte 135243

Ich bin überrascht, dass niemand daran gedacht hat, die iter Zwei-Argument-Form zu verwenden:

from itertools import islice

def chunk(it, size):
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())

Demo:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]

Dies funktioniert mit jedem Iterable und gibt die Ausgabe träge aus. Es gibt Tupel zurück anstatt Iteratoren, aber ich denke, es hat dennoch eine gewisse Eleganz. Es füllt auch nicht auf; wenn Sie eine Füllung möchten, reicht eine einfache Variation des obigen aus:

from itertools import islice, chain, repeat

def chunk_pad(it, size, padval=None):
    it = chain(iter(it), repeat(padval))
    return iter(lambda: tuple(islice(it, size)), (padval,) * size)

Demo:

>>> list(chunk_pad(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk_pad(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

Wie die auf izip_longest basierenden Lösungen füllt das obige immer. Soweit ich weiß, gibt es kein ein- oder zwei-Zeilen-Itertools-Rezept für eine Funktion, die optional füllt. Durch die Kombination der obigen beiden Ansätze kommt diese ziemlich nah dran:

_no_padding = object()

def chunk(it, size, padval=_no_padding):
    if padval == _no_padding:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(padval))
        sentinel = (padval,) * size
    return iter(lambda: tuple(islice(it, size)), sentinel)

Demo:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
>>> list(chunk(range(14), 3, None))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

Ich glaube, dass dies der kürzeste vorgeschlagene Chunker ist, der optionale Füllung ermöglicht.

Wie Tomasz Gandor beobachtet hat, werden die beiden Padding-Chunker unerwartet stoppen, wenn sie auf eine lange Sequenz von Füllwerten treffen. Hier ist eine letzte Variation, die dieses Problem auf vernünftige Weise umgeht:

_no_padding = object()
def chunk(it, size, padval=_no_padding):
    it = iter(it)
    chunker = iter(lambda: tuple(islice(it, size)), ())
    if padval == _no_padding:
        yield from chunker
    else:
        for ch in chunker:
            yield ch if len(ch) == size else ch + (padval,) * (size - len(ch))

Demo:

>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]

12 Stimmen

Wunderbar, deine einfache Version ist mein Favorit. Andere kamen auch auf den grundlegenden islice(it, size) Ausdruck und haben ihn (wie ich es getan hatte) in eine Schleifenstruktur eingebettet. Nur du hast an die zwei-Argument Version von iter() gedacht (von der ich vollkommen nichts wusste), was es super-elegant (und wahrscheinlich auch am effektivsten) macht. Ich hatte keine Ahnung, dass das erste Argument von iter zu einer 0-Argument Funktion wird, wenn das Stoppzeichen gegeben wird. Du gibst einen (potenziell unendlichen) Iterator von Chunks zurück, kannst einen (potenziell unendlichen) Iterator als Eingabe verwenden, hast kein len() und keine Array-Slices. Genial!

3 Stimmen

Einzeiler-Version: ``` from itertools import islice from functools import partial seq = [1,2,3,4,5,6,7] size = 3 result = list(iter(partial(lambda it: tuple(islice(it, size)), iter(seq)), ())) assert result == [(1, 2, 3), (4, 5, 6), (7,)] ```

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X