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)]
Come zip(*[iter(s)]*n)
funziona? Come sarebbe se fosse scritto con un codice più dettagliato?
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)]
Come zip(*[iter(s)]*n)
funziona? Come sarebbe se fosse scritto con un codice più dettagliato?
Risposte:
iter()
è un iteratore su una sequenza. [x] * n
produce una lista contenente la n
quantità di x
, cioè una lista di lunghezza n
, dove si trova ogni elemento x
. *arg
decomprime una sequenza in argomenti per una chiamata di funzione. Quindi stai passando lo stesso iteratore 3 volte a zip()
, e ogni volta estrae un elemento dall'iteratore.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
yield
s (= return
s) un oggetto, puoi immaginarlo come "consumato". Quindi la prossima volta che viene chiamato l'iteratore, restituisce l'elemento successivo "non consumato".
Le altre ottime risposte e commenti spiegano bene i ruoli dell'argomento unpacking e zip () .
Come dicono Ignacio e ujukatzel , passi a zip()
tre riferimenti allo stesso iteratore e fai zip()
3-tuple degli interi, in ordine, da ogni riferimento all'iteratore:
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
^ ^ ^
^ ^ ^
^ ^ ^
E poiché chiedi un esempio di codice più dettagliato:
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
Seguendo i valori di start
e end
:
[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]
FWIW, puoi ottenere lo stesso risultato map()
con un argomento iniziale di None
:
>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Per ulteriori informazioni su zip()
e map()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/
Penso che una cosa che manca in tutte le risposte (probabilmente ovvia per chi ha familiarità con gli iteratori) ma non così ovvia per gli altri è:
Poiché abbiamo lo stesso iteratore, viene consumato e gli elementi rimanenti vengono utilizzati dallo zip. Quindi, se usassimo semplicemente l'elenco e non l'iter, ad es.
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)]
Utilizzando l'iteratore, visualizza i valori e mantiene solo la disponibilità, quindi per zip una volta consumato 0 è disponibile 1, quindi 2 e così via. Una cosa molto sottile, ma abbastanza intelligente !!!
iter(s)
restituisce un iteratore per s.
[iter(s)]*n
crea una lista di n volte lo stesso iteratore per s.
Quindi, quando lo fa zip(*[iter(s)]*n)
, estrae un elemento da tutti e tre gli iteratori dall'elenco in ordine. Poiché tutti gli iteratori sono lo stesso oggetto, raggruppa semplicemente l'elenco in blocchi di n
.
Un consiglio per usare zip in questo modo. Troncerà l'elenco se la sua lunghezza non è divisibile in modo uniforme. Per aggirare questo problema, puoi utilizzare itertools.izip_longest se puoi accettare i valori di riempimento. Oppure potresti usare qualcosa del genere:
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:], ]
Uso:
for ints in n_split(range(1,12), 3):
print ', '.join([str(i) for i in ints])
stampe:
1, 2, 3
4, 5, 6
7, 8, 9
10, 11
itertools
ricette: docs.python.org/2/library/itertools.html#recipes grouper
. Non c'è bisogno di reinventare la ruota
Probabilmente è più facile vedere cosa sta succedendo nell'interprete Python o ipython
con n = 2
:
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]
Quindi, abbiamo un elenco di due iteratori che puntano allo stesso oggetto iteratore. Ricorda che iter
su un oggetto restituisce un oggetto iteratore e in questo scenario, è lo stesso iteratore due volte a causa dello *2
zucchero sintattico Python. Anche gli iteratori vengono eseguiti una sola volta.
Inoltre, zip
accetta un numero qualsiasi di iterabili (le sequenze sono iterabili ) e crea una tupla dall'i-esimo elemento di ciascuna delle sequenze di input. Poiché entrambi gli iteratori sono identici nel nostro caso, zip sposta lo stesso iteratore due volte per ogni tupla di output a 2 elementi.
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.
L' operatore unpacking ( *
) assicura che gli iteratori vengano eseguiti fino all'esaurimento, cosa che in questo caso è fino a quando non c'è abbastanza input per creare una tupla a 2 elementi.
Questo può essere esteso a qualsiasi valore di n
e zip(*[iter(s)]*n)
funziona come descritto.
*
è solo comodità per duplicare un oggetto. Provalo con gli scalari e poi con le liste. Prova anche a print(*zip(*[iter("ABCDEFG")]*2))
vs print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))
. Quindi inizia a ridurre i due in passaggi più piccoli per vedere quali sono gli oggetti iteratori effettivi nelle due istruzioni.