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)]
¿Cómo zip(*[iter(s)]*n)
funciona? ¿Cómo se vería si estuviera escrito con un código más detallado?
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)]
¿Cómo zip(*[iter(s)]*n)
funciona? ¿Cómo se vería si estuviera escrito con un código más detallado?
Respuestas:
iter()
es un iterador sobre una secuencia. [x] * n
produce una lista que contiene la n
cantidad de x
, es decir, una lista de longitud n
, donde está cada elemento x
. *arg
descomprime una secuencia en argumentos para una llamada a función. Por lo tanto, está pasando el mismo iterador 3 veces a zip()
, y cada vez extrae un elemento del iterador.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
yield
s (= return
s) de un elemento, se puede imaginar este artículo como "consumido". Por tanto, la próxima vez que se llame al iterador, se obtendrá el siguiente elemento "no consumido".
Las otras excelentes respuestas y comentarios explican bien los roles de descomprimir argumentos y zip () .
Como dicen Ignacio y ujukatzel , pasas a zip()
tres referencias al mismo iterador y haces zip()
3 tuplas de los enteros, en orden, de cada referencia al iterador:
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
^ ^ ^
^ ^ ^
^ ^ ^
Y dado que solicita una muestra de código más detallada:
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
Siguiendo los valores de start
y end
:
[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]
FWIW, puede obtener el mismo resultado map()
con un argumento inicial de None
:
>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Para obtener más información sobre zip()
y map()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/
Creo que una cosa que se perdió en todas las respuestas (probablemente obvia para aquellos familiarizados con los iteradores) pero no tan obvia para otros es:
Como tenemos el mismo iterador, se consume y el zip utiliza los elementos restantes. Entonces, si simplemente usamos la lista y no el iter, por ejemplo.
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)]
Usando el iterador, muestra los valores y solo permanece disponible, por lo que para zip, una vez que se consume 0, 1 está disponible y luego 2 y así sucesivamente. ¡Una cosa muy sutil, pero bastante inteligente!
iter(s)
devuelve un iterador para s.
[iter(s)]*n
hace una lista de n veces el mismo iterador para s.
Entonces, al hacerlo zip(*[iter(s)]*n)
, extrae un elemento de los tres iteradores de la lista en orden. Dado que todos los iteradores son el mismo objeto, solo agrupa la lista en partes de n
.
Un consejo para usar zip de esta manera. Truncará su lista si su longitud no es divisible de manera uniforme. Para solucionar esto, puede usar itertools.izip_longest si puede aceptar valores de relleno. O podrías usar algo como esto:
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])
Huellas dactilares:
1, 2, 3
4, 5, 6
7, 8, 9
10, 11
itertools
recetas: docs.python.org/2/library/itertools.html#recipes grouper
. No es necesario reinventar la rueda
Probablemente sea más fácil ver lo que está sucediendo en el intérprete de Python o ipython
con n = 2
:
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]
Entonces, tenemos una lista de dos iteradores que apuntan al mismo objeto iterador. Recuerde que iter
en un objeto devuelve un objeto iterador y, en este escenario, es el mismo iterador dos veces debido al *2
azúcar sintáctico de Python. Los iteradores también se ejecutan solo una vez.
Además, zip
toma cualquier número de iterables (las secuencias son iterables ) y crea una tupla a partir del primer elemento de cada una de las secuencias de entrada. Dado que ambos iteradores son idénticos en nuestro caso, zip mueve el mismo iterador dos veces por cada tupla de salida de 2 elementos.
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.
El operador unpacking ( *
) asegura que los iteradores se agoten, lo que en este caso es hasta que no haya suficiente entrada para crear una tupla de 2 elementos.
Esto se puede extender a cualquier valor n
y zip(*[iter(s)]*n)
funciona como se describe.
*
es solo una conveniencia para duplicar un objeto. Pruébelo con escalares y luego con listas. Proveedores print(*zip(*[iter("ABCDEFG")]*2))
vs print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))
. Luego comience a dividir los dos en pasos más pequeños para ver cuáles son los objetos iteradores en las dos declaraciones.