Aunque es muy intrigante, esta técnica debe ir en contra del valor central de "legibilidad" de Python.
Demis
Respuestas:
108
iter()es un iterador sobre una secuencia. [x] * nproduce una lista que contiene la ncantidad de x, es decir, una lista de longitud n, donde está cada elemento x. *argdescomprime 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.
Es bueno saberlo: cuando un iterador yields (= returns) 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".
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:
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 3for start in range(0,len(L),chunk_size):# xrange() in 2.x; range() in 3.x
end = start + chunk_sizeprint L[start:end]# three-item chunks
Siguiendo los valores de starty 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:
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!
+1, ¡me salvaste! No puedo creer que otras respuestas omitieran este detalle vital asumiendo que todos lo saben. ¿Puede dar alguna referencia a una documentación que incluya esta información?
Snehasish Karmakar
9
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.
No 'n iteradores de la misma lista', sino 'n veces el mismo objeto iterador'. Los diferentes objetos de iterador no comparten el estado, incluso cuando pertenecen a la misma lista.
Thomas Wouters
Gracias, corregido. De hecho, eso era lo que estaba "pensando", pero escribí algo más.
sttwister
6
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:
Probablemente sea más fácil ver lo que está sucediendo en el intérprete de Python o ipythoncon n = 2:
In[35]:[iter("ABCDEFGH")]*2Out[35]:[<iterator at 0x6be4128>,<iterator at 0x6be4128>]
Entonces, tenemos una lista de dos iteradores que apuntan al mismo objeto iterador. Recuerde que iteren un objeto devuelve un objeto iterador y, en este escenario, es el mismo iterador dos veces debido al *2azúcar sintáctico de Python. Los iteradores también se ejecutan solo una vez.
Además, ziptoma 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 ny zip(*[iter(s)]*n)funciona como se describe.
Perdón por ser lento. Pero, ¿podría explicar "el mismo iterador dos veces debido al azúcar sintáctico de Python * 2. Los iteradores también se ejecutan solo una vez". parte por favor? Si es así, ¿cómo es que el resultado no es [("A", "A") ....]? Gracias.
Bowen Liu
@BowenLiu *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.
Respuestas:
iter()
es un iterador sobre una secuencia.[x] * n
produce una lista que contiene lan
cantidad dex
, es decir, una lista de longitudn
, donde está cada elementox
.*arg
descomprime una secuencia en argumentos para una llamada a función. Por lo tanto, está pasando el mismo iterador 3 veces azip()
, y cada vez extrae un elemento del iterador.fuente
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 haceszip()
3 tuplas de los enteros, en orden, de cada referencia al iterador:Y dado que solicita una muestra de código más detallada:
Siguiendo los valores de
start
yend
:FWIW, puede obtener el mismo resultado
map()
con un argumento inicial deNone
:Para obtener más información sobre
zip()
ymap()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/fuente
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.
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!
fuente
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 den
.fuente
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:
Uso:
Huellas dactilares:
fuente
itertools
recetas: docs.python.org/2/library/itertools.html#recipesgrouper
. No es necesario reinventar la ruedaProbablemente sea más fácil ver lo que está sucediendo en el intérprete de Python o
ipython
conn = 2
: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.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
yzip(*[iter(s)]*n)
funciona como se describe.fuente
*
es solo una conveniencia para duplicar un objeto. Pruébelo con escalares y luego con listas. Proveedoresprint(*zip(*[iter("ABCDEFG")]*2))
vsprint(*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.