s = [1,2,3,4,5,6,7,8,9]
n = 3
zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]
Wie funktioniert das zip(*[iter(s)]*n)
? Wie würde es aussehen, wenn es mit ausführlicherem Code geschrieben würde?
s = [1,2,3,4,5,6,7,8,9]
n = 3
zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]
Wie funktioniert das zip(*[iter(s)]*n)
? Wie würde es aussehen, wenn es mit ausführlicherem Code geschrieben würde?
Antworten:
iter()
ist ein Iterator über eine Sequenz. [x] * n
erzeugt eine Liste mit einer n
Menge von x
, dh einer Liste der Länge n
, in der sich jedes Element befindet x
. *arg
entpackt eine Sequenz in Argumente für einen Funktionsaufruf. Daher übergeben Sie denselben Iterator dreimal an zip()
und er zieht jedes Mal ein Element aus dem Iterator.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
yield
(= return
s) ist, können Sie sich dieses Element als "verbraucht" vorstellen. Wenn der Iterator das nächste Mal aufgerufen wird, ergibt er das nächste "nicht verbrauchte" Element.
Die anderen tollen Antworten und Kommentare erklären gut die Rolle des Auspackens von Argumenten und von zip () .
Wie Ignacio und Ujukatzel sagen, gehen Sie zu zip()
drei Verweisen auf denselben Iterator über und erstellenzip()
aus jedem Verweis auf den Iterator 3-Tupel der Ganzzahlen - in der Reihenfolge -:
1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9
^ ^ ^
^ ^ ^
^ ^ ^
Und da Sie nach einem ausführlicheren Codebeispiel fragen:
chunk_size = 3
L = [1,2,3,4,5,6,7,8,9]
# iterate over L in steps of 3
for start in range(0,len(L),chunk_size): # xrange() in 2.x; range() in 3.x
end = start + chunk_size
print L[start:end] # three-item chunks
Folgen Sie den Werten von start
und end
:
[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]
FWIW, Sie können das gleiche Ergebnis map()
mit einem anfänglichen Argument von erhalten None
:
>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Weitere Informationen zu zip()
und map()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/
Ich denke, eine Sache, die in allen Antworten übersehen wird (wahrscheinlich offensichtlich für diejenigen, die mit Iteratoren vertraut sind), aber für andere nicht so offensichtlich ist:
Da wir denselben Iterator haben, wird er verbraucht und die restlichen Elemente werden von der Zip-Datei verwendet. Wenn wir also einfach die Liste und nicht den Iter verwenden, z.
l = range(9)
zip(*([l]*3)) # note: not an iter here, the lists are not emptied as we iterate
# output
[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8)]
Wenn Sie den Iterator verwenden, werden die Werte eingeblendet und bleiben nur verfügbar, sodass für zip, sobald 0 verbraucht ist, 1 verfügbar ist und dann 2 und so weiter. Eine sehr subtile Sache, aber ziemlich klug !!!
iter(s)
Gibt einen Iterator für s zurück.
[iter(s)]*n
macht eine Liste von n mal dem gleichen Iterator für s.
Dabei zip(*[iter(s)]*n)
wird ein Element aus allen drei Iteratoren der Reihe nach aus der Liste extrahiert. Da alle Iteratoren dasselbe Objekt sind, wird die Liste nur in Blöcke von gruppiert n
.
Ein Ratschlag für die Verwendung von zip auf diese Weise. Ihre Liste wird abgeschnitten, wenn ihre Länge nicht gleichmäßig teilbar ist. Um dies zu umgehen , können Sie entweder itertools.izip_longest verwenden, wenn Sie Füllwerte akzeptieren können. Oder Sie könnten so etwas verwenden:
def n_split(iterable, n):
num_extra = len(iterable) % n
zipped = zip(*[iter(iterable)] * n)
return zipped if not num_extra else zipped + [iterable[-num_extra:], ]
Verwendung:
for ints in n_split(range(1,12), 3):
print ', '.join([str(i) for i in ints])
Drucke:
1, 2, 3
4, 5, 6
7, 8, 9
10, 11
itertools
Rezepten dokumentiert : docs.python.org/2/library/itertools.html#recipes grouper
. Keine Notwendigkeit, das Rad neu zu erfinden
Es ist wahrscheinlich einfacher zu sehen, was im Python-Interpreter passiert oder ipython
mit n = 2
:
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]
Wir haben also eine Liste von zwei Iteratoren, die auf dasselbe Iteratorobjekt zeigen. Denken Sie daran, dass iter
ein Objekt ein Iteratorobjekt zurückgibt und in diesem Szenario aufgrund des *2
syntaktischen Python-Zuckers zweimal derselbe Iterator ist . Iteratoren laufen auch nur einmal.
Ferner zip
nimmt eine beliebige Anzahl von Iterables ( Sequenzen sind Iterables ) und erstellt Tupel aus i'th Elemente von jedem der Eingangssequenzen. Da in unserem Fall beide Iteratoren identisch sind, verschiebt zip denselben Iterator zweimal für jedes 2-Element-Tupel der Ausgabe.
In [41]: help(zip)
Help on built-in function zip in module __builtin__:
zip(...)
zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
Return a list of tuples, where each tuple contains the i-th element
from each of the argument sequences. The returned list is truncated
in length to the length of the shortest argument sequence.
Der Operator unpacking ( *
) stellt sicher, dass die Iteratoren bis zur Erschöpfung ausgeführt werden. In diesem Fall ist dies so lange der Fall, bis nicht mehr genügend Eingaben vorhanden sind, um ein Tupel mit zwei Elementen zu erstellen.
Dies kann auf jeden Wert von erweitert werden n
und zip(*[iter(s)]*n)
funktioniert wie beschrieben.
*
ist nur eine bequeme Möglichkeit, ein Objekt zu duplizieren. Versuchen Sie es mit Skalaren und dann mit Listen. Versuchen Sie auch print(*zip(*[iter("ABCDEFG")]*2))
vs print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))
. Zerreißen Sie dann die beiden in kleinere Schritte, um zu sehen, was die tatsächlichen Iteratorobjekte in den beiden Anweisungen sind.