Ich habe ein Python-Skript, das als Eingabe eine Liste von Ganzzahlen verwendet, die ich mit jeweils vier Ganzzahlen bearbeiten muss. Leider habe ich keine Kontrolle über die Eingabe, oder ich hätte sie als Liste von Tupeln mit vier Elementen übergeben. Derzeit iteriere ich folgendermaßen darüber:
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]
Es sieht jedoch sehr nach "C-think" aus, was mich vermuten lässt, dass es eine pythonischere Art gibt, mit dieser Situation umzugehen. Die Liste wird nach dem Iterieren verworfen, sodass sie nicht beibehalten werden muss. Vielleicht wäre so etwas besser?
while ints:
foo += ints[0] * ints[1] + ints[2] * ints[3]
ints[0:4] = []
Trotzdem fühlt es sich nicht ganz richtig an. : - /
Verwandte Frage: Wie teilt man eine Liste in Python in gleich große Teile auf?
Antworten:
Geändert von den Rezepten Abschnitt von Pythons itertools docs:
Beispiel
Im Pseudocode, um das Beispiel knapp zu halten.
Hinweis: Verwenden Sie unter Python 2
izip_longest
anstelle vonzip_longest
.quelle
izip_longest
256.000 Argumente eingespeist werden.None
den letzten Block aufzufüllen?Einfach. Einfach. Schnell. Funktioniert mit jeder Sequenz:
quelle
itertools
Modul nicht vertraut sind, etwas dunkel (unklar) sein könnte .chunker
a zurückgegeben wirdgenerator
. Ersetzen Sie die Rückkehr zu:return [...]
um eine Liste zu erhalten.yield
:for pos in xrange(0, len(seq), size): yield seq[pos:pos + size]
. Ich bin mir nicht sicher, ob dies intern in einem relevanten Aspekt anders gehandhabt wird, aber es könnte sogar ein bisschen klarer sein.__getitem__
Methode unterstützen.Ich bin ein Fan von
quelle
chunk
1, 2 oder 3 Elemente für den letzten Elementstapel. In dieser Frage erfahren Sie, warum Slice-Indizes außerhalb der Grenzen liegen können .Ein anderer Weg:
quelle
size
, was manchmal wünschenswert ist.len
Anruf und funktionieren daher nicht mit anderen Generatoren.quelle
izip_longest
durchzip_longest
Die ideale Lösung für dieses Problem funktioniert mit Iteratoren (nicht nur Sequenzen). Es sollte auch schnell sein.
Dies ist die Lösung, die in der Dokumentation für itertools bereitgestellt wird:
Wenn ich Ipythons
%timeit
auf meinem Mac Book Air verwende, erhalte ich 47,5 US-Dollar pro Schleife.Dies funktioniert jedoch wirklich nicht für mich, da die Ergebnisse so gepolstert sind, dass sie gleich große Gruppen sind. Eine Lösung ohne Polsterung ist etwas komplizierter. Die naivste Lösung könnte sein:
Einfach, aber ziemlich langsam: 693 us pro Schleife
Die beste Lösung, die ich
islice
für die innere Schleife finden konnte:Mit dem gleichen Datensatz erhalte ich 305 us pro Schleife.
Da es nicht möglich ist, eine reine Lösung schneller zu erhalten, biete ich der folgenden Lösung eine wichtige Einschränkung: Wenn Ihre Eingabedaten Instanzen enthalten
filldata
, können Sie eine falsche Antwort erhalten.Diese Antwort gefällt mir wirklich nicht, aber sie ist deutlich schneller. 124 us pro Schleife
quelle
itertools
Importe;map
muss Py3 seinmap
oderimap
) :def grouper(n, it): return takewhile(bool, map(tuple, starmap(islice, repeat((iter(it), n)))))
. Ihre letzte Funktion kann durch Verwendung eines Sentinels weniger spröde gemacht werden: Entfernen Sie dasfillvalue
Argument; Fügen Sie eine erste Zeile hinzufillvalue = object()
und ändern Sie dann dasif
Häkchen inif i[-1] is fillvalue:
und die Zeile, in die es steuertyield tuple(v for v in i if v is not fillvalue)
. Garantiert, dass kein Wert initerable
mit dem Füllwert verwechselt werden kann.islice
Objekten verliert (# 3 gewinnt, wennn
es relativ groß ist, z. B. die Anzahl der Gruppen ist klein, aber das optimiert für einen ungewöhnlichen Fall), aber ich habe nicht erwartet, dass es so ist extrem.izip_longest
des endgültigen Tupels :yield i[:modulo]
.args
Tupeln Sie die Variable anstelle einer Liste :args = (iter(iterable),) * n
. Rasiert noch ein paar Taktzyklen aus. Wenn wir den Füllwert ignorieren und annehmenNone
, kann die Bedingungif None in i
für noch mehr Taktzyklen gelten.yield
), während der häufige Fall nicht betroffen ist.Ich brauchte eine Lösung, die auch mit Sets und Generatoren funktioniert. Ich konnte mir nichts sehr kurzes und hübsches einfallen lassen, aber es ist zumindest gut lesbar.
Liste:
Einstellen:
Generator:
quelle
Ähnlich wie bei anderen Vorschlägen, aber nicht genau identisch, mache ich das gerne so, weil es einfach und leicht zu lesen ist:
Auf diese Weise erhalten Sie nicht den letzten Teilblock. Wenn Sie
(9, None, None, None)
als letzten Block erhalten möchten , verwenden Sie einfachizip_longest
vonitertools
.quelle
zip(*([it]*4))
Wenn es Ihnen nichts ausmacht, ein externes Paket zu verwenden, können Sie es
iteration_utilities.grouper
ab 1 verwenden . Es unterstützt alle iterablen Elemente (nicht nur Sequenzen):iteration_utilties
welche druckt:
Falls die Länge nicht ein Vielfaches der Gruppengröße ist, unterstützt sie auch das Füllen (die unvollständige letzte Gruppe) oder das Abschneiden (das Verwerfen der unvollständigen letzten Gruppe) der letzten:
Benchmarks
Ich habe mich auch entschlossen, die Laufzeit einiger der genannten Ansätze zu vergleichen. Es handelt sich um ein Protokoll-Protokoll-Diagramm, das anhand einer Liste unterschiedlicher Größe in Gruppen von "10" Elementen gruppiert wird. Für qualitative Ergebnisse: Niedriger bedeutet schneller:
Zumindest in diesem Benchmark
iteration_utilities.grouper
schneidet der am besten ab. Gefolgt von der Annäherung von Craz .Der Benchmark wurde mit 1 erstellt . Der Code, der zum Ausführen dieses Benchmarks verwendet wurde, war:
simple_benchmark
1 Haftungsausschluss: Ich bin der Autor der Bibliotheken
iteration_utilities
undsimple_benchmark
.quelle
Da es noch niemand erwähnt hat, ist hier eine
zip()
Lösung:Dies funktioniert nur, wenn die Länge Ihrer Sequenz immer durch die Blockgröße teilbar ist oder Sie sich nicht für einen nachfolgenden Block interessieren, wenn dies nicht der Fall ist.
Beispiel:
Oder verwenden Sie itertools.izip , um einen Iterator anstelle einer Liste zurückzugeben:
Die Polsterung kann mit der Antwort von @ ΤΖΩΤΖΙΟΥ behoben werden :
quelle
Die Verwendung von map () anstelle von zip () behebt das Auffüllproblem in JF Sebastians Antwort:
Beispiel:
quelle
itertools.izip_longest
(Py2) /itertools.zip_longest
(Py3) gehandhabt; Diese Verwendung vonmap
ist doppelt veraltet und in Py3 nicht verfügbar (Sie können sie nichtNone
als Mapper-Funktion übergeben. Sie wird beendet, wenn die kürzeste iterierbare Funktion erschöpft ist, nicht die längste; sie wird nicht aufgefüllt).Ein anderer Ansatz wäre die Verwendung der Zwei-Argumente-Form von
iter
:Dies kann leicht angepasst werden, um Polsterung zu verwenden (dies ähnelt der Antwort von Markus Jarderot ):
Diese können sogar für eine optionale Polsterung kombiniert werden:
quelle
Wenn die Liste groß ist, ist die Verwendung eines Generators die leistungsstärkste Methode, um dies zu tun:
quelle
iterable = range(100000000)
&chunksize
bis zu 10000.Die Verwendung kleiner Funktionen und Dinge gefällt mir wirklich nicht. Ich bevorzuge es, nur Scheiben zu verwenden:
quelle
len
. Sie können einen Test mititertools.repeat
oder durchführenitertools.cycle
.[...for...]
(...for...)
So vermeiden Sie alle Konvertierungen in eine Liste
import itertools
und:Produziert:
Ich habe es überprüft
groupby
und es wird nicht in eine Liste konvertiert oder verwendetlen
Ich denke, dies verzögert die Auflösung jedes Werts, bis er tatsächlich verwendet wird. Leider schien keine der verfügbaren Antworten (zu diesem Zeitpunkt) diese Variation zu bieten.Wenn Sie nacheinander mit jedem Element umgehen müssen, verschachteln Sie natürlich eine for-Schleife über g:
Mein besonderes Interesse daran war die Notwendigkeit, einen Generator zu verwenden, um Änderungen in Stapeln von bis zu 1000 an die Google Mail-API zu senden:
quelle
groupby(messages, lambda x: x/3)
wären, würden Sie einen TypeError (für den Versuch, eine Zeichenfolge durch ein int zu teilen) und keine Gruppierungen mit drei Buchstaben erhalten. Wenn du es getangroupby(enumerate(messages), lambda x: x[0]/3)
hättest, hättest du vielleicht etwas. Aber das hast du in deinem Beitrag nicht gesagt.Mit NumPy ist es einfach:
Ausgabe:
quelle
quelle
Sofern ich nichts vermisse, wurde die folgende einfache Lösung mit Generatorausdrücken nicht erwähnt. Es wird davon ausgegangen, dass sowohl die Größe als auch die Anzahl der Chunks bekannt sind (was häufig der Fall ist) und dass keine Polsterung erforderlich ist:
quelle
Bei Ihrer zweiten Methode würde ich auf diese Weise zur nächsten 4er-Gruppe übergehen:
Ich habe jedoch keine Leistungsmessung durchgeführt, daher weiß ich nicht, welche effizienter sein könnte.
Trotzdem würde ich normalerweise die erste Methode wählen. Es ist nicht schön, aber das ist oft eine Folge der Schnittstelle zur Außenwelt.
quelle
Noch eine Antwort, deren Vorteile sind:
1) Leicht verständlich
2) Funktioniert mit allen iterierbaren Sequenzen, nicht nur mit Sequenzen (einige der oben genannten Antworten ersticken an Dateihandles)
3) Lädt den Block nicht auf einmal in den Speicher
4) Erstellt keine lange Liste von Verweisen auf der gleiche Iterator im Speicher
5) Kein Auffüllen der Füllwerte am Ende der Liste
Davon abgesehen habe ich es nicht zeitlich festgelegt, sodass es möglicherweise langsamer ist als einige der clevereren Methoden, und einige der Vorteile sind angesichts des Anwendungsfalls möglicherweise irrelevant.
Update:
Einige Nachteile aufgrund der Tatsache, dass die innere und die äußere Schleife Werte vom selben Iterator abrufen:
1) Fortfahren funktioniert in der äußeren Schleife nicht wie erwartet - es wird nur mit dem nächsten Element fortgefahren, anstatt einen Block zu überspringen . Dies scheint jedoch kein Problem zu sein, da in der äußeren Schleife nichts zu testen ist.
2) break funktioniert im inneren Regelkreis nicht wie erwartet - die Steuerung wird mit dem nächsten Element im Iterator wieder im inneren Regelkreis beendet. Um ganze Blöcke zu überspringen, wickeln Sie entweder den inneren Iterator (ii oben) in ein Tupel, z. B.
for c in tuple(ii)
, oder setzen Sie ein Flag und erschöpfen Sie den Iterator.quelle
quelle
Sie können die Partitions- oder Chunks- Funktion aus der Funcy- Bibliothek verwenden:
Diese Funktionen haben auch Iteratorversionen
ipartition
undichunks
, die in diesem Fall effizienter sind.Sie können auch einen Blick auf deren Implementierung werfen .
quelle
Über die
J.F. Sebastian
hier gegebene Lösung :Es ist klug, hat aber einen Nachteil - immer Tupel zurückgeben. Wie bekomme ich stattdessen einen String?
Natürlich kannst du schreiben
''.join(chunker(...))
, aber das temporäre Tupel ist trotzdem aufgebaut.Sie können das temporäre Tupel loswerden, indem Sie Folgendes schreiben
zip
:Dann
Anwendungsbeispiel:
quelle
zip
scheint es nicht die beste Idee zu sein, das vorhandene neu zu schreiben, anstatt es zu verwenden.Ich mag diesen Ansatz. Es fühlt sich einfach und nicht magisch an und unterstützt alle iterierbaren Typen und erfordert keine Importe.
quelle
Ich möchte nie, dass meine Brocken gepolstert werden, daher ist diese Anforderung unerlässlich. Ich finde, dass die Fähigkeit, an jedem Iterable zu arbeiten, ebenfalls Voraussetzung ist. Angesichts dessen habe ich mich entschlossen, die akzeptierte Antwort https://stackoverflow.com/a/434411/1074659 zu erweitern .
Die Leistung nimmt bei diesem Ansatz leicht ab, wenn keine Auffüllung gewünscht wird, da die aufgefüllten Werte verglichen und gefiltert werden müssen. Für große Blockgrößen ist dieses Dienstprogramm jedoch sehr leistungsfähig.
quelle
Hier ist ein Chunker ohne Importe, der Generatoren unterstützt:
Anwendungsbeispiel:
quelle
Mit Python 3.8 können Sie den Walross-Operator und verwenden
itertools.islice
.quelle
Es scheint keinen schönen Weg zu geben, dies zu tun. Hier ist eine Seite mit einer Reihe von Methoden, darunter:
quelle
Wenn die Listen dieselbe Größe haben, können Sie sie zu Listen mit 4 Tupeln mit kombinieren
zip()
. Zum Beispiel:Folgendes
zip()
erzeugt die Funktion:Wenn die Listen groß sind und Sie sie nicht zu einer größeren Liste kombinieren möchten, verwenden Sie
itertools.izip()
, wodurch anstelle einer Liste ein Iterator erstellt wird.quelle
Einzeilige Ad-hoc-Lösung zum Durchlaufen einer Liste
x
in großen Stücken4
-quelle