A menudo, he encontrado la necesidad de procesar una lista por pares. Me preguntaba cuál sería la forma pitónica y eficiente de hacerlo, y encontré esto en Google:
pairs = zip(t[::2], t[1::2])
Pensé que era lo suficientemente pitónico, pero después de una discusión reciente que involucró modismos versus eficiencia , decidí hacer algunas pruebas:
import time
from itertools import islice, izip
def pairs_1(t):
return zip(t[::2], t[1::2])
def pairs_2(t):
return izip(t[::2], t[1::2])
def pairs_3(t):
return izip(islice(t,None,None,2), islice(t,1,None,2))
A = range(10000)
B = xrange(len(A))
def pairs_4(t):
# ignore value of t!
t = B
return izip(islice(t,None,None,2), islice(t,1,None,2))
for f in pairs_1, pairs_2, pairs_3, pairs_4:
# time the pairing
s = time.time()
for i in range(1000):
p = f(A)
t1 = time.time() - s
# time using the pairs
s = time.time()
for i in range(1000):
p = f(A)
for a, b in p:
pass
t2 = time.time() - s
print t1, t2, t2-t1
Estos fueron los resultados en mi computadora:
1.48668909073 2.63187503815 1.14518594742
0.105381965637 1.35109519958 1.24571323395
0.00257992744446 1.46182489395 1.45924496651
0.00251388549805 1.70076990128 1.69825601578
Si los estoy interpretando correctamente, eso debería significar que la implementación de listas, indexación de listas y división de listas en Python es muy eficiente. Es un resultado reconfortante e inesperado.
¿Existe otra forma "mejor" de recorrer una lista por parejas?
Tenga en cuenta que si la lista tiene un número impar de elementos, el último no estará en ninguno de los pares.
¿Cuál sería la forma correcta de garantizar que se incluyan todos los elementos?
Agregué estas dos sugerencias de las respuestas a las pruebas:
def pairwise(t):
it = iter(t)
return izip(it, it)
def chunkwise(t, size=2):
it = iter(t)
return izip(*[it]*size)
Estos son los resultados:
0.00159502029419 1.25745987892 1.25586485863
0.00222492218018 1.23795199394 1.23572707176
Resultados hasta ahora
Más pitónico y muy eficiente:
pairs = izip(t[::2], t[1::2])
Más eficiente y muy pitónico:
pairs = izip(*[iter(t)]*2)
Me tomó un momento asimilar que la primera respuesta usa dos iteradores mientras que la segunda usa uno solo.
Para tratar con secuencias con un número impar de elementos, la sugerencia ha sido aumentar la secuencia original agregando un elemento ( None
) que se empareja con el último elemento anterior, algo con lo que se puede lograr itertools.izip_longest()
.
Finalmente
Tenga en cuenta que, en Python 3.x, se zip()
comporta como itertools.izip()
y itertools.izip()
se ha ido.