2010-02-20 31 views
36

¿Existe alguna forma sintácticamente más concisa de escribir lo siguiente?Obtenga el enésimo artículo de un generador en Python

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 debe tener una expresión gen[index], que actúa como una lista, pero es funcionalmente idéntico al código de seguridad.

+8

Usted no quiere 'is' en esta situación (o muchas situaciones en absoluto). 'is' es para comparar identidad, no igualdad. Quieres '=='. Esto probablemente funcionará en esta instancia, pero solo por coincidencia y detalles de implementación. –

+1

Como uso números enteros, ¿cómo podría no funcionar? ¿Es incluso una buena práctica esperar que el objeto 'index' implemente' __eq__' en casos como este? (Esto se está saliendo del tema ...) –

+2

Pruebe '1000 es 500 + 500', será (probablemente)' False'. Ver, por ejemplo, http://stackoverflow.com/questions/306313/python-is-operator-behaves-unexpectedly-with-integers –

Respuesta

35

un método sería utilizar itertools.islice

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

Ni siquiera sabía acerca de la función 'next()' tampoco. ¡Gracias! –

+3

Reemplazar '5 + 1' con' None' también funciona, y lee –

-1

Tal vez deba explicar más sobre un caso de uso real.

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

He editado 'xrange (10)' a '(i para i en xrange (10))'. Resulta que esta sintaxis funciona para 'xrange' ya que no es realmente un generador ... –

+5

' xrange' es anterior a los generadores, y devuelve un objeto xrange, que en realidad implementa el protocolo de secuencia completa. –

14

Se podría hacer esto, usando count como un generador ejemplo:

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

¿Qué versión de Python es esta? El código anterior me da el error 'AttributeError: 'itertools.islice' object no tiene el atributo 'next'' en 3.3. – LarsH

+0

En Python 3x, cambie 'next' por' __next __() ', es decir,' islice (count, n, n = 1) .__ next __() ' – Mohammed

+2

Por lo tanto, es mejor usar' next (islice (count(), n , n + 1)) '. –

-2

que sólo puede convertir el generador en una lista y utilice el índice como normal:

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

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

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

Pero recuerde, los generadores no son como las listas. No almacenan sus resultados intermedios en ninguna parte, por lo que no puede retroceder. Voy a demostrar el problema con un ejemplo sencillo en el repl pitón:

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

vez que empiece a ir a través de un generador para obtener el valor de orden n en la secuencia, el generador se encuentra ahora en un estado diferente, e intentar obtener el enésimo valor nuevamente le devolverá un resultado diferente, lo que probablemente resultará en un error en su código.

Echemos un vistazo a otro ejemplo, basado en el código de la pregunta.

Inicialmente se esperaba 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 escribir esto en el repl y se obtiene:

>>> 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 trazando ese insecto hacia abajo.

0

El 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 
Cuestiones relacionadas