2011-01-19 13 views
110

que tienen una función de generador como el siguiente:cómo elegir solo un artículo de un generador (en python)?

def myfunct(): 
    ... 
    yield result 

La forma habitual de llamar a esta función sería:

for r in myfunct(): 
    dostuff(r) 

Mi pregunta, ¿hay una manera de conseguir sólo un elemento del generador cada vez que me gusta? Por ejemplo, me gustaría hacer algo como:

while True: 
    ... 
    if something: 
     my_element = pick_just_one_element(myfunct()) 
     dostuff(my_element) 
    ... 

Respuesta

161

Crear un generador utilizando

g = myfunct() 

Cada vez que le gustaría un elemento, utilice

g.next() 

o

next(g) 

If el generador sale, aumentará StopIteration. Usted puede detectar esta excepción si es necesario, o utilizar el argumento default a next():

next(g, default_value) 
+3

Nota, solo aumentará StopIteration cuando intente usar g.next() después de que se haya proporcionado el último elemento en g. – Wilduck

+15

'next (gen, default)' también se puede usar para evitar la excepción 'StopIteration'. Por ejemplo 'next (g, None)' para un generador de cadenas producirá una cadena o None una vez que la iteración haya finalizado. – Attila

+4

en Python 3000, siguiente() es __next __() –

-1

Creo que la única manera es conseguir una lista desde el iterador a continuación, obtener el elemento que desee de la lista.

l = list(myfunct()) 
l[4] 
+0

La respuesta de Sven es probablemente mejor, pero dejaré esto aquí en caso de que esté más en línea con sus necesidades. – keegan3d

+22

Asegúrese de tener un generador finito antes de hacer esto. – Seth

+5

Disculpe, esto tiene la complejidad de la longitud del iterador, mientras que el problema es obviamente O (1). –

2

No creo que haya una manera conveniente de recuperar un valor arbitrario de un generador. El generador proporcionará un método next() para atravesarlo, pero la secuencia completa no se produce inmediatamente para guardar la memoria. Esa es la diferencia funcional entre un generador y una lista.

0
generator = myfunct() 
while True: 
    my_element = generator.next() 

asegurarse de atrapar la excepción lanzada después de que el último elemento se toma

+0

No es válido para Python 3, consulte la excelente [respuesta por kxr] (http://stackoverflow.com/a/35370041/260122). – clacke

12

Para recoger sólo un elemento de un uso generador de break en un comunicado for, o list(itertools.islice(gen, 1))

De acuerdo con el ejemplo (literalmente) se puede hacer como:

while True: 
    ... 
    if something: 
     for my_element in myfunct(): 
      dostuff(my_element) 
      break 
     else: 
      do_generator_empty() 

Si desea "obtener jus t un elemento del [una vez generada] generador cuando me gusta "(supongo 50% eso es la intención original, y la intención más común), entonces:

gen = myfunct() 
while True: 
    ... 
    if something: 
     for my_element in gen: 
      dostuff(my_element) 
      break 
     else: 
      do_generator_empty() 

esta manera uso explícito de generator.next() puede haber evitado, y el manejo del final de la entrada no requiere (críptico) StopIteration manejo de excepciones o comparaciones de valores por defecto adicionales.

La sección de enunciado else: de solo es necesaria si desea hacer algo especial en caso de fin de generador.

Nota sobre next()/.next():

En python3 el método .next() se renombró a .__next__() por una buena razón: su bajo nivel considerado (PEP 3114). Antes de Python 2.6, la función integrada next() no existía. Y hasta se discutió para mover next() al módulo operator (lo que habría sido acertado), debido a su rara necesidad y al cuestionable inflado de los nombres incorporados.

El uso de next() sigue siendo una práctica de nivel muy bajo - arrojando el críptico StopIteration como un perno de la nada en el código de aplicación normal abiertamente. Y usar next() con centinela predeterminado - que mejor debería ser la única opción para un next() directamente en builtins - es limitado y a menudo le da razón a la lógica/legibilidad no-pitónica impar.

En pocas palabras: Usar next() debería ser muy raro, como usar las funciones del módulo operator. Usar for x in iterator, islice, list(iterator) y otras funciones que acepten un iterador sin problemas es la forma natural de usar iteradores en el nivel de aplicación, y siempre es posible. next() es de bajo nivel, un concepto adicional, no obvio, como lo muestra la pregunta de este hilo. Mientras, por ejemplo, utilizando break en for es convencional.

+0

Esto es demasiado trabajo para obtener el primer elemento de un resultado de la lista. A menudo no necesito que sea flojo, pero no tengo opción en py3. ¿No hay algo parecido a 'mySeq.head'? – javadba

Cuestiones relacionadas