2010-02-09 12 views
80
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?¿Cómo funciona zip (* [iter (s)] * n) en Python?

+1

también eche un vistazo aquí donde también se explica cómo funciona: http://stackoverflow.com/questions/2202461/yield-multiple-objects-at-a-time-from-an-iterable-object/2202485# 2202485 –

+0

si las respuestas aquí no son suficientes, lo publiqué aquí: http://telliott99.blogspot.com/2010/01/chunks-of-sequence-in-python.html – telliott99

+5

Aunque es muy intrigante, esta técnica debe ir en contra el valor central de "legibilidad" de Python! – Demis

Respuesta

82

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 desempaqueta una secuencia en argumentos para una llamada de función. Por lo tanto, está pasando el mismo iterador 3 veces al 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) 
7

iter(s) devuelve un iterador para s.

[iter(s)]*n hace una lista de n veces el mismo iterador para s.

Por lo tanto, 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.

+4

No 'n iteradores de la misma lista', pero 'n veces el mismo objeto iterador'. Los diferentes objetos iteradores no comparten el estado, incluso cuando pertenecen a la misma lista. –

+0

Gracias, corregido. De hecho, eso era lo que estaba "pensando", pero escribí algo más. – sttwister

37

Las otras grandes respuestas y comentarios explican bien los roles de argument unpacking y zip().

Como Ignacio y ujukatzel decir, se pasa a zip() tres referencias a la misma iterador y zip() hace 3-tuplas de los enteros-fin-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 usted pedir un código de ejemplo 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, se 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 map(): http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/

+12

No son tres copias del mismo iterador, es tres veces el mismo objeto iterador :) –

5

Un consejo para el uso postal presente camino. Truncará su lista si su longitud no es divisible de manera uniforme. Para solucionar este problema, puede usar itertools.izip_longest si puede aceptar valores de relleno.O usted podría utilizar 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:], ] 

de uso:

for ints in n_split(range(1,12), 3): 
    print ', '.join([str(i) for i in ints]) 

Prints:

1, 2, 3 
4, 5, 6 
7, 8, 9 
10, 11 
+2

Esto ya está documentado en las recetas de 'itertools': http://docs.python.org/2/library/itertools.html#recipes' mero'. No es necesario reinventar la rueda – jamylak

21

Creo que una cosa que se perdió en todas las respuestas (probablemente obvio para aquellos familiarizados con iteradores) pero no tan obvio para otros es -

Como tenemos el mismo iterador, se obtiene s consumido y los elementos restantes son utilizados por el zip. Entonces, si simplemente usamos la lista y no el iter , por ej.

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 iterador, estallidos los valores y sólo mantiene restante disponible, así que para postal una vez que se consume 0 1 está disponible y luego 2 y así sucesivamente. ¡Una cosa muy sutil, pero bastante inteligente!

+0

+1, ¡me salvaste! No puedo creer que otras respuestas omitieran este detalle vital suponiendo que todo el mundo lo sabe. ¿Puede dar alguna referencia a una documentación que incluye esta información? –