nathan fellman
Tengo dos iterables y quiero repasarlos en pares:
foo = [1, 2, 3]
bar = [4, 5, 6]
for (f, b) in iterate_together(foo, bar):
print("f: ", f, "; b: ", b)
Eso debería resultar en:
f: 1; b: 4
f: 2; b: 5
f: 3; b: 6
Una La forma de hacerlo es iterar sobre los índices:
for i in range(len(foo)):
print("f: ", foo[i], "; b: ", bar[i])
Pero eso me parece algo poco pitónico. Hay una mejor manera de hacerlo?
unutbu
Pitón 3
for f, b in zip(foo, bar):
print(f, b)
zip
se detiene cuando el más corto de foo
o bar
se detiene
En Pitón 3, zip
devuelve un iterador de tuplas, como itertools.izip
en Python2. Para obtener una lista de tuplas, utilice list(zip(foo, bar))
. Y para comprimir hasta que se agoten ambos iteradores, usaría
itertools.zip_más largo.
Pitón 2
En Pitón 2, zip
devuelve una lista de tuplas. Esto está bien cuando foo
y bar
no son masivos. Si ambos son masivos entonces forman zip(foo,bar)
es una variable temporal innecesariamente masiva, y debe ser reemplazada por itertools.izip
o
itertools.izip_longest
que devuelve un iterador en lugar de una lista.
import itertools
for f,b in itertools.izip(foo,bar):
print(f,b)
for f,b in itertools.izip_longest(foo,bar):
print(f,b)
izip
se detiene cuando foo
o bar
está agotado.
izip_longest
se detiene cuando ambos foo
y bar
están agotados. Cuando se agotan los iteradores más cortos, izip_longest
produce una tupla con None
en la posición correspondiente a ese iterador. También puede establecer un diferente fillvalue
además None
si lo desea. Ver aquí para el historia completa.
Tenga en cuenta también que zip
y es zip
-like brethen puede aceptar un número arbitrario de iterables como argumentos. Por ejemplo,
for num, cheese, color in zip([1,2,3], ['manchego', 'stilton', 'brie'],
['red', 'blue', 'green']):
print('{} {} {}'.format(num, color, cheese))
huellas dactilares
1 red manchego
2 blue stilton
3 green brie
-
@unutbu ¿Por qué preferiría el método de OP sobre el
izip
uno (aunque elizip
/zip
se ve mucho más limpio)?– armar
14/03/2016 a las 19:23
-
Es posible que desee mencionar Python 3 primero, ya que probablemente esté más preparado para el futuro. Además, vale la pena señalar que en Python 3, zip() tiene exactamente esa ventaja que solo itertools.izip() tenía en Python 2 y, por lo tanto, suele ser el camino a seguir.
– daniel s.
14/06/2016 a las 17:40
-
¿Puedo pedirle que actualice su respuesta para indicar explícitamente que
zip
yzip
-como funciones deitertools
¿Aceptar cualquier número de iterables y no solo 2? Esta pregunta es canónica ahora y su respuesta es la única que vale la pena actualizar.– bóveda
11 de julio de 2016 a las 15:01
-
que pasa si ademas quiero el indice
i
? ¿Puedo envolver ese zip en enumerar?–Charlie Parker
06/03/2018 a las 18:05
-
@CharlieParker: Sí, puedes, pero entonces usarías
for i, (f, b) in enumerate(zip(foo, bar))
.– unutbu
06/03/2018 a las 19:20
Quiere que el zip
función.
for (f,b) in zip(foo, bar):
print "f: ", f ,"; b: ", b
-
Antes de Python 3.0 querrías usar
itertools.izip
si tiene un gran número de elementos.– Georg Scholly
2 de noviembre de 2009 a las 21:35
Deberías usar ‘Código Postal‘ función. Aquí hay un ejemplo de cómo puede verse su propia función zip
def custom_zip(seq1, seq2):
it1 = iter(seq1)
it2 = iter(seq2)
while True:
yield next(it1), next(it2)
-
¿No tiene esto exactamente el mismo resultado que
zip(seq1, seq2)
?– Niklas Mertsch
6 de junio de 2018 a las 9:35
-
@NiklasMertsch sí, tiene exactamente el mismo resultado. Acabo de proporcionar un ejemplo de cómo se ve la función zip
– Vlad Bezden
6 jun 2018 a las 15:40
-
Esta es una reinvención bastante limitada de
zip
y la redacción es bastante engañosa. Si va a reinventar la rueda (no, es una función integrada, no una dependencia), al menos esta respuesta acepta un número variable de iterables y generalmente se comporta como cabría esperarzip
a.– ggorlen
7 de febrero de 2021 a las 4:27
Oso del sol
Sobre la base de la respuesta de @unutbu, comparé el rendimiento de iteración de dos listas idénticas al usar Python 3.6 zip()
funciones, Python enumerate()
función, utilizando un contador manual (ver count()
función), utilizando una lista de índice, y durante un escenario especial donde los elementos de una de las dos listas (ya sea foo
o bar
) se puede utilizar para indexar la otra lista. Sus desempeños para imprimir y crear una nueva lista, respectivamente, fueron investigados usando el timeit()
función donde el número de repeticiones utilizadas fue de 1000 veces. A continuación se muestra uno de los scripts de Python que había creado para realizar estas investigaciones. Los tamaños de los foo
y bar
las listas habían oscilado entre 10 y 1.000.000 de elementos.
Resultados:
-
Para fines de impresión: Se observó que los rendimientos de todos los enfoques considerados eran aproximadamente similares a los
zip()
después de factorizar una tolerancia de precisión de +/-5%. Se produjo una excepción cuando el tamaño de la lista era inferior a 100 elementos. En tal escenario, el método de lista de índice fue ligeramente más lento que elzip()
funcionar mientras elenumerate()
la función fue ~9% más rápida. Los otros métodos arrojaron un rendimiento similar alzip()
función. -
Para crear listas: Se exploraron dos tipos de enfoques de creación de listas: usando el (a)
list.append()
método y (b) lista de comprensión. Después de factorizar una tolerancia de precisión de +/-5%, para ambos enfoques, elzip()
Se encontró que la función funcionaba más rápido que laenumerate()
función, que usar un índice de lista, que usar un contador manual. La ganancia de rendimiento por parte delzip()
la función en estas comparaciones puede ser entre un 5 % y un 60 % más rápida. Curiosamente, usando el elemento defoo
al Indicebar
puede producir rendimientos equivalentes o más rápidos (5% a 20%) que elzip()
función.
Dar sentido a estos resultados:
Un programador tiene que determinar la cantidad de tiempo de cómputo por operación que sea significativa o que sea significativa.
Por ejemplo, para propósitos de impresión, si este criterio de tiempo es 1 segundo, es decir, 10**0 segundos, entonces mirando el eje y del gráfico que está a la izquierda en 1 segundo y proyectándolo horizontalmente hasta que alcance las curvas monomiales , vemos que los tamaños de listas que tienen más de 144 elementos incurrirán en un costo de cómputo significativo y significarán para el programador. Es decir, cualquier rendimiento obtenido por los enfoques mencionados en esta investigación para tamaños de lista más pequeños será insignificante para el programador. El programador concluirá que el desempeño del zip()
La función para iterar declaraciones de impresión es similar a los otros enfoques.
Conclusión
Se puede obtener un rendimiento notable utilizando el zip()
función para iterar a través de dos listas en paralelo durante list
creación. Al iterar a través de dos listas en paralelo para imprimir los elementos de las dos listas, el zip()
La función producirá un rendimiento similar al de la enumerate()
función, en cuanto al uso de una variable de contador manual, en cuanto al uso de una lista de índice, y en cuanto a durante el escenario especial donde los elementos de una de las dos listas (ya sea foo
o bar
) se puede utilizar para indexar la otra lista.
El script de Python 3.6 que se usó para investigar la creación de listas.
import timeit
import matplotlib.pyplot as plt
import numpy as np
def test_zip( foo, bar ):
store = []
for f, b in zip(foo, bar):
#print(f, b)
store.append( (f, b) )
def test_enumerate( foo, bar ):
store = []
for n, f in enumerate( foo ):
#print(f, bar[n])
store.append( (f, bar[n]) )
def test_count( foo, bar ):
store = []
count = 0
for f in foo:
#print(f, bar[count])
store.append( (f, bar[count]) )
count += 1
def test_indices( foo, bar, indices ):
store = []
for i in indices:
#print(foo[i], bar[i])
store.append( (foo[i], bar[i]) )
def test_existing_list_indices( foo, bar ):
store = []
for f in foo:
#print(f, bar[f])
store.append( (f, bar[f]) )
list_sizes = [ 10, 100, 1000, 10000, 100000, 1000000 ]
tz = []
te = []
tc = []
ti = []
tii= []
tcz = []
tce = []
tci = []
tcii= []
for a in list_sizes:
foo = [ i for i in range(a) ]
bar = [ i for i in range(a) ]
indices = [ i for i in range(a) ]
reps = 1000
tz.append( timeit.timeit( 'test_zip( foo, bar )',
'from __main__ import test_zip, foo, bar',
number=reps
)
)
te.append( timeit.timeit( 'test_enumerate( foo, bar )',
'from __main__ import test_enumerate, foo, bar',
number=reps
)
)
tc.append( timeit.timeit( 'test_count( foo, bar )',
'from __main__ import test_count, foo, bar',
number=reps
)
)
ti.append( timeit.timeit( 'test_indices( foo, bar, indices )',
'from __main__ import test_indices, foo, bar, indices',
number=reps
)
)
tii.append( timeit.timeit( 'test_existing_list_indices( foo, bar )',
'from __main__ import test_existing_list_indices, foo, bar',
number=reps
)
)
tcz.append( timeit.timeit( '[(f, b) for f, b in zip(foo, bar)]',
'from __main__ import foo, bar',
number=reps
)
)
tce.append( timeit.timeit( '[(f, bar[n]) for n, f in enumerate( foo )]',
'from __main__ import foo, bar',
number=reps
)
)
tci.append( timeit.timeit( '[(foo[i], bar[i]) for i in indices ]',
'from __main__ import foo, bar, indices',
number=reps
)
)
tcii.append( timeit.timeit( '[(f, bar[f]) for f in foo ]',
'from __main__ import foo, bar',
number=reps
)
)
print( f'te = {te}' )
print( f'ti = {ti}' )
print( f'tii = {tii}' )
print( f'tc = {tc}' )
print( f'tz = {tz}' )
print( f'tce = {te}' )
print( f'tci = {ti}' )
print( f'tcii = {tii}' )
print( f'tcz = {tz}' )
fig, ax = plt.subplots( 2, 2 )
ax[0,0].plot( list_sizes, te, label="enumerate()", marker="." )
ax[0,0].plot( list_sizes, ti, label="index-list", marker="." )
ax[0,0].plot( list_sizes, tii, label="element of foo", marker="." )
ax[0,0].plot( list_sizes, tc, label="count()", marker="." )
ax[0,0].plot( list_sizes, tz, label="zip()", marker=".")
ax[0,0].set_xscale('log')
ax[0,0].set_yscale('log')
ax[0,0].set_xlabel('List Size')
ax[0,0].set_ylabel('Time (s)')
ax[0,0].legend()
ax[0,0].grid( b=True, which="major", axis="both")
ax[0,0].grid( b=True, which="minor", axis="both")
ax[0,1].plot( list_sizes, np.array(te)/np.array(tz), label="enumerate()", marker="." )
ax[0,1].plot( list_sizes, np.array(ti)/np.array(tz), label="index-list", marker="." )
ax[0,1].plot( list_sizes, np.array(tii)/np.array(tz), label="element of foo", marker="." )
ax[0,1].plot( list_sizes, np.array(tc)/np.array(tz), label="count()", marker="." )
ax[0,1].set_xscale('log')
ax[0,1].set_xlabel('List Size')
ax[0,1].set_ylabel('Performances ( vs zip() function )')
ax[0,1].legend()
ax[0,1].grid( b=True, which="major", axis="both")
ax[0,1].grid( b=True, which="minor", axis="both")
ax[1,0].plot( list_sizes, tce, label="list comprehension using enumerate()", marker=".")
ax[1,0].plot( list_sizes, tci, label="list comprehension using index-list()", marker=".")
ax[1,0].plot( list_sizes, tcii, label="list comprehension using element of foo", marker=".")
ax[1,0].plot( list_sizes, tcz, label="list comprehension using zip()", marker=".")
ax[1,0].set_xscale('log')
ax[1,0].set_yscale('log')
ax[1,0].set_xlabel('List Size')
ax[1,0].set_ylabel('Time (s)')
ax[1,0].legend()
ax[1,0].grid( b=True, which="major", axis="both")
ax[1,0].grid( b=True, which="minor", axis="both")
ax[1,1].plot( list_sizes, np.array(tce)/np.array(tcz), label="enumerate()", marker="." )
ax[1,1].plot( list_sizes, np.array(tci)/np.array(tcz), label="index-list", marker="." )
ax[1,1].plot( list_sizes, np.array(tcii)/np.array(tcz), label="element of foo", marker="." )
ax[1,1].set_xscale('log')
ax[1,1].set_xlabel('List Size')
ax[1,1].set_ylabel('Performances ( vs zip() function )')
ax[1,1].legend()
ax[1,1].grid( b=True, which="major", axis="both")
ax[1,1].grid( b=True, which="minor", axis="both")
plt.show()
don f
Puede agrupar los n-ésimos elementos en una tupla o lista utilizando la comprensión y luego distribuirlos con una función generadora.
def iterate_multi(*lists):
for i in range(min(map(len,lists))):
yield tuple(l[i] for l in lists)
for l1, l2, l3 in iterate_multi([1,2,3],[4,5,6],[7,8,9]):
print(str(l1)+","+str(l2)+","+str(l3))
Pedro Mortensen
He aquí cómo hacerlo con un lista de comprensión:
a = (1, 2, 3)
b = (4, 5, 6)
[print('f:', i, '; b', j) for i, j in zip(a, b)]
Imprime:
f: 1 ; b 4
f: 2 ; b 5
f: 3 ; b 6
Pedro Mortensen
Podemos usar un índice para iterar…
foo = ['a', 'b', 'c']
bar = [10, 20, 30]
for indx, itm in enumerate(foo):
print (foo[indx], bar[indx])
-
Por que usar
enumerate
si en realidad no estás usandoitm
? O cambia aprint(itm, bar[index])
o simplemente hacer un bucle comofor indx in range(len(foo))
– Tomerikoo
8 de febrero a las 5:14