Iterar una lista como par (actual, siguiente) en Python

A veces necesito iterar una lista en Python mirando el elemento “actual” y el elemento “siguiente”. Lo he hecho, hasta ahora, con código como:

for current, next in zip(the_list, the_list[1:]): # Do something 

Esto funciona y hace lo que espero, pero ¿hay una forma más idiomática o eficiente de hacer lo mismo?

Aquí hay un ejemplo relevante de los documentos del módulo itertools :

 import itertools def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = itertools.tee(iterable) next(b, None) return zip(a, b) 

Para Python 2, necesita itertools.izip lugar de zip :

 import itertools def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = itertools.tee(iterable) next(b, None) return itertools.izip(a, b) 

Cómo funciona esto:

Primero, se crean dos iteradores paralelos, b (la llamada tee() ), ambos apuntando al primer elemento del iterable original. El segundo iterador, b se mueve 1 paso adelante (la next(b, None) llamada next(b, None) ). En este punto, a apunta a s0 y b apunta a s1. Tanto a como b pueden atravesar el iterador original de forma independiente: la función izip toma los dos iteradores y hace pares de los elementos devueltos, avanzando ambos iteradores al mismo ritmo.

Una advertencia: la función tee() produce dos iteradores que pueden avanzar independientemente uno del otro, pero tiene un costo. Si uno de los iteradores avanza más que el otro, entonces tee() necesita mantener los elementos consumidos en la memoria hasta que el segundo iterador los consum también (no puede “rebobinar” el iterador original). Aquí no importa porque un iterador está solo un paso por delante del otro, pero en general es fácil usar mucha memoria de esta manera.

Y como tee() puede tomar un parámetro n , esto también se puede usar para más de dos iteradores paralelos:

 def threes(iterator): "s -> (s0,s1,s2), (s1,s2,s3), (s2, s3,4), ..." a, b, c = itertools.tee(iterator, 3) next(b, None) next(c, None) next(c, None) return zip(a, b, c) 

¡Rueda tu propio!

 def pairwise(iterable): it = iter(iterable) a = next(it, None) for b in it: yield (a, b) a = b 

Dado que the_list[1:] crea realmente una copia de toda la lista (excluyendo su primer elemento), y zip() crea una lista de tuplas inmediatamente cuando se las llama, en total se crean tres copias de su lista. Si tu lista es muy grande, quizás prefieras

 from itertools import izip, islice for current_item, next_item in izip(the_list, islice(the_list, 1, None)): print(current_item, next_item) 

que no copia la lista en absoluto.

Iterar por índice puede hacer lo mismo:

 #!/usr/bin/python the_list = [1, 2, 3, 4] for i in xrange(len(the_list) - 1): current_item, next_item = the_list[i], the_list[i + 1] print(current_item, next_item) 

Salida:

 (1, 2) (2, 3) (3, 4) 

Solo estoy publicando esto, estoy muy sorprendido de que nadie haya pensado en enumerar ().

 for (index, thing) in enumerate(the_list): if index < len(the_list): current, next_ = thing, the_list[index + 1] #do something 

Pares de una lista usando una lista de comprensión

 the_list = [1, 2, 3, 4] pairs = [[the_list[i], the_list[i + 1]] for i in range(len(the_list) - 1)] for [current_item, next_item] in pairs: print(current_item, next_item) 

Salida:

 (1, 2) (2, 3) (3, 4) 

Una solución básica:

 def neighbors( list ): i = 0 while i + 1 < len( list ): yield ( list[ i ], list[ i + 1 ] ) i += 1 for ( x, y ) in neighbors( list ): print( x, y ) 
 code = '0016364ee0942aa7cc04a8189ef3' # Getting the current and next item print [code[idx]+code[idx+1] for idx in range(len(code)-1)] # Getting the pair print [code[idx*2]+code[idx*2+1] for idx in range(len(code)/2)]