2011-01-19 15 views
110

Ho una funzione generatore simile al seguente:come scegliere un solo oggetto da un generatore (in python)?

def myfunct(): 
    ... 
    yield result 

Il solito modo per chiamare questa funzione potrebbe essere:

for r in myfunct(): 
    dostuff(r) 

La mia domanda, c'è un modo per ottenere solo un elemento dal generatore quando mi piace? Per esempio, mi piacerebbe fare qualcosa di simile:

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

risposta

161

Creare un generatore utilizzando

g = myfunct() 

Ogni volta che si desidera un elemento, utilizzare

g.next() 

o

next(g) 

Se il generatore esce, solleverà StopIteration. È possibile prendere questa eccezione, se necessario, o usare l'argomento default a next():

next(g, default_value) 
+3

Nota, solleverà StopIteration solo quando si tenta di utilizzare g.next() dopo che è stato fornito l'ultimo elemento in g. – Wilduck

+15

'next (gen, default)' può anche essere usato per evitare l'eccezione 'StopIteration'. Ad esempio "next (g, None)" per un generatore di stringhe produrrà una stringa o None dopo che l'iterazione è stata completata. – Attila

+4

in Python 3000, next() è __next __() –

-1

Credo che l'unico modo è quello di ottenere un elenco dal iteratore quindi ottenere l'elemento che si desidera da quella lista.

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

La risposta di Sven è probabilmente migliore, ma lascerò questo qui perché è più in linea con le tue esigenze. – keegan3d

+22

Assicurarsi di avere un generatore finito prima di farlo. – Seth

+5

Siamo spiacenti, questo ha la complessità della lunghezza dell'iteratore, mentre il problema è ovviamente O (1). –

2

Non credo che ci sia un modo conveniente per recuperare un valore arbitrario da un generatore. Il generatore fornirà un metodo next() per attraversare se stesso, ma la sequenza completa non viene prodotta immediatamente per risparmiare memoria. Questa è la differenza funzionale tra un generatore e un elenco.

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

assicurarsi di catturare l'eccezione generata dopo l'ultimo elemento è preso

+0

Non valido per Python 3, vedere l'eccellente [risposta di kxr] (http://stackoverflow.com/a/35370041/260122). – clacke

12

Per raccogliere solo un elemento di un utilizzo generatore break in una dichiarazione for, o list(itertools.islice(gen, 1))

Secondo il vostro esempio (letteralmente) si può fare come:

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

Se si vuole "get jus t un elemento dal [una volta generate] generatore ogni volta mi piace "(suppongo il 50% questo è l'intenzione originale, e l'intenzione più comune), allora:

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

In questo modo l'uso esplicito di generator.next() può essere evitato e la gestione di fine input non richiede (criptica) la gestione delle eccezioni StopIteration o confronti di valori di default aggiuntivi.

La sezione di istruzioni else: di for è necessaria solo se si desidera fare qualcosa di speciale in caso di fine del generatore.

Note on next()/.next():

Nel python3 metodo .next() è stato rinominato .__next__() per una buona ragione: la sua considerata di basso livello (PEP 3114). Prima di Python 2.6 la funzione incorporata next() non esisteva. Ed è stato anche discusso di spostare next() nel modulo operator (che sarebbe stato saggio), a causa della sua rara necessità e dell'inflazione discutibile dei nomi di costruzione.

L'utilizzo di next() senza impostazione predefinita è ancora una pratica di livello molto basso - lanciando il criptico StopIteration come un fulmine fuori dal blu nel normale codice di applicazione apertamente. E usando next() con sentinella di default - quale migliore dovrebbe essere l'unica opzione per un next() direttamente in builtins - è limitato e spesso dà ragione a strane logiche/leggibilità non-pythonic.

Linea inferiore: l'utilizzo di next() dovrebbe essere molto raro, ad esempio utilizzando le funzioni del modulo operator. L'utilizzo di for x in iterator, islice, list(iterator) e altre funzioni che accettano senza problemi un iteratore è il modo naturale di utilizzare gli iteratori a livello di applicazione, e tutto ciò è sempre possibile. next() è di basso livello, un concetto in più, non ovvio - come mostra la domanda di questo thread. Ad esempio, ad es. utilizzando break in for è convenzionale.

+0

Questo è troppo lavoro per ottenere solo il primo elemento di un risultato di lista. Spesso non ho bisogno che sia pigro ma non ho scelta in py3. Non c'è qualcosa di simile a 'mySeq.head'? – javadba

Problemi correlati