range python
¿Cómo funciona zip(*[iter(s)]*n) en Python? (5)
Creo que una cosa que se ha omitido en todas las respuestas (probablemente obvia para quienes están familiarizados con los iteradores) pero no tan obvio para los demás es:
Como tenemos el mismo iterador, se consume y el zip usa los elementos restantes. Entonces, si simplemente usamos la lista y no la 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)]
Al usar el iterador, aparece los valores y solo queda disponible, por lo que para el zip una vez que se consume 0, 1 está disponible y luego 2, y así sucesivamente. ¡Una cosa muy sutil, pero bastante inteligente!
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 funciona zip(*[iter(s)]*n)
? ¿Qué aspecto tendría si estuviera escrito con un código más detallado?
Las otras excelentes respuestas y comentarios explican bien los roles del argumento desempaquetar y zip() .
Como dicen Ignacio y ujukatzel , pasas a zip()
tres referencias al mismo iterador y zip()
hace 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 ya que pides una muestra de código más detallado:
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 con map()
con un argumento inicial de None
:
>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Para más información sobre zip()
y el map()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/
Una palabra de consejo para usar zip de esta manera. Truncará su lista si su longitud no es divisible de manera uniforme. Para itertools.izip_longest este problema, 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
iter()
es un iterador sobre una secuencia. [x] * n
produce una lista que contiene n
cantidad de x
, es decir, una lista de longitud n
, donde cada elemento es x
. *arg
descomprime una secuencia en argumentos para una llamada de función. Por lo tanto, está pasando el mismo iterador 3 veces a zip()
, y extrae un elemento del iterador cada vez.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
iter(s)
devuelve un iterador para s.
[iter(s)]*n
hace una lista de n veces el mismo iterador para s.
Entonces, al hacer zip(*[iter(s)]*n)
, extrae un elemento de los tres iteradores de la lista en orden. Como todos los iteradores son el mismo objeto, simplemente agrupa la lista en fragmentos de n
.