Obtener el artículo nth de un generador en Python

¿Hay una forma sintácticamente más concisa de escribir lo siguiente?

gen = (i for i in xrange(10)) index = 5 for i, v in enumerate(gen): if i is index: return v 

Parece casi natural que un generador tenga una expresión gen[index] , que actúa como una lista, pero que sea funcionalmente idéntica al código anterior.

Un método sería utilizar itertools.islice

 >>> next(itertools.islice(xrange(10), 5, 5 + 1)) 5 

Puedes hacer esto, usando la count como un generador de ejemplo:

 from itertools import islice, count next(islice(count(), n, n+1)) 

Creo que la mejor manera es:

 next(x for i,x in enumerate(it) if i==n) 

(Donde está su iterador y n es el índice)

No requiere que agregue una importación (como las soluciones que usan itertools ) ni que cargue todos los elementos del iterador en la memoria a la vez (como las soluciones que usan la list ).

Nota 1: esta versión StopIteration un error StopIteration si su iterador tiene menos de n elementos. Si quiere obtener None lugar, puede usar:

 next((x for i,x in enumerate(it) if i==n), None) 

Nota 2: No hay paréntesis dentro de la llamada a la next . Esto no es una comprensión de lista, sino una comprensión de generador, que no consume el iterador original más allá de su elemento n.

Discutiría contra la tentación de tratar a los generadores como listas. El enfoque simple pero ingenuo es el simple de una sola línea:

 gen = (i for i in range(10)) list(gen)[3] 

Pero recuerda, los generadores no son como listas. No almacenan sus resultados intermedios en ningún lugar, por lo que no puede retroceder. Demostraré el problema con un ejemplo simple en la respuesta de python:

 >>> gen = (i for i in range(10)) >>> list(gen)[3] 3 >>> list(gen)[3] Traceback (most recent call last): File "", line 1, in  IndexError: list index out of range 

Una vez que comience a pasar por un generador para obtener el enésimo valor de la secuencia, el generador se encuentra ahora en un estado diferente, y al intentar obtener el enésimo valor nuevamente le devolverá un resultado diferente, lo que probablemente resulte en un error en su código.

Veamos otro ejemplo, basado en el código de la pregunta.

Uno esperaría inicialmente lo siguiente para imprimir 4 dos veces.

 gen = (i for i in range(10)) index = 4 for i, v in enumerate(gen): if i == index: answer = v break print(answer) for i, v in enumerate(gen): if i == index: answer = v break print(answer) 

pero escribe esto en la respuesta y obtendrás:

 >>> gen = (i for i in range(10)) >>> index = 4 >>> for i, v in enumerate(gen): ... if i == index: ... answer = v ... break ... >>> print(answer) 4 >>> for i, v in enumerate(gen): ... if i == index: ... answer = v ... break ... >>> print(answer) 9 

Buena suerte rastreando ese error.

EDITAR:

Como se señaló, si el generador es infinitamente largo, ni siquiera se puede convertir a una lista. La list(gen) expresiones list(gen) nunca terminará.

Hay una manera de poner un envoltorio de almacenamiento en caché evaluado perezosamente alrededor de un generador infinito para que se vea como una lista infinitamente larga en la que se podría indexar a voluntad, pero eso merece su propia pregunta y respuesta, y tendría importantes implicaciones en el rendimiento.

Lo mejor de usar es: ejemplo:

 a = gen values ('a','c','d','e') 

entonces la respuesta será:

 a = list(a) -> this will convert the generator to a list (it will store in memory) 

entonces cuando quieras ir al índice específico vas a:

 a[INDEX] -> and you will able to get the value its holds 

si desea conocer solo el conteo o realizar operaciones que no se requieren almacenar en la memoria, las mejores prácticas serán: a = sum(1 in i in a) -> esto contará la cantidad de objetos que tiene

Espero haberlo hecho más simple.

Lo primero que me vino a la mente fue:

 gen = (i for i in xrange(10)) index = 5 for i, v in zip(range(index), gen): pass return v 

Tal vez debería elaborar más sobre un caso de uso real.

 >>> gen = xrange(10) >>> ind=5 >>> gen[ind] 5 

simplemente puede convertir el generador en una lista y usar el índice normalmente:

 >>> [i for i in range(10)][index] 5