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.
timeit
módulo.Respuestas:
Mi forma favorita de hacerlo:
Cuando desee emparejar todos los elementos, obviamente es posible que necesite un valor de relleno:
fuente
itertools
sección de recetas.izip(*[iter(t)]*size)
Yo diría que su solución inicial
pairs = zip(t[::2], t[1::2])
es la mejor porque es más fácil de leer (y en Python 3,zip
devuelve automáticamente un iterador en lugar de una lista).Para asegurarse de que todos los elementos estén incluidos, simplemente puede ampliar la lista en
None
.Entonces, si la lista tiene un número impar de elementos, el último par será
(item, None)
.fuente
Empiezo con un pequeño descargo de responsabilidad: no use el código a continuación. No es Pythonic en absoluto, escribí solo por diversión. Es similar a la
pairwise
función @ THC4k pero usaiter
ylambda
cierra. No usaitertools
módulo y no es compatiblefillvalue
. Lo puse aquí porque alguien podría encontrarlo interesante:fuente
En lo que respecta a la mayoría de Pythonic, diría que las recetas proporcionadas en los documentos fuente de Python (algunas de las cuales se parecen mucho a las respuestas que @JochenRitzel proporcionó) son probablemente su mejor opción;)
En Python moderno, solo tiene que usarlo de
zip_longest(*args, fillvalue=fillvalue)
acuerdo con la página del documento correspondiente .fuente
No puedo decirlo con certeza, pero lo dudo: cualquier otro recorrido incluiría más código Python que debe interpretarse. Las funciones integradas como zip () están escritas en C, que es mucho más rápido.
Verifique la longitud de la lista y si es impar (
len(list) & 1 == 1
), copie la lista y agregue un elemento.fuente
fuente
Solo hazlo:
fuente
list(zip(l, l[1:]))
y no divide la lista en pares.Aquí hay un ejemplo de cómo crear pares / piernas usando un generador. Los generadores están libres de límites de pila
Ejemplo:
Salida:
fuente
[(0, 1), (2, 3), (4, 5)....
zip()
ya devuelve un generador en Python 3.x, @VladBezdenEn caso de que alguien necesite el algoritmo de respuesta, aquí está:
Pero tenga en cuenta que su lista original también se reducirá a su último elemento, porque utilizó
pop
en él.fuente