2010-02-20 7 views
36

Esiste un modo più sinteticamente conciso per scrivere quanto segue?Ottieni l'ennesimo elemento di un generatore in Python

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

Sembra quasi naturale che un generatore dovrebbe avere un'espressione gen[index], che agisce come un elenco, ma è funzionalmente identico al codice di cui sopra.

+8

Non si vuole 'is' in questa situazione (o molte situazioni del tutto). 'is' è per confrontare l'identità, non l'uguaglianza. Vuoi '=='. Questo probabilmente funzionerà in questo caso, ma solo per coincidenza e dettaglio di implementazione. –

+1

Poiché utilizzo gli interi, come potrebbe non funzionare? È addirittura buona norma aspettarsi che l'oggetto 'index' implementa' __eq__' in casi come questo? (Questo sta diventando fuori tema ...) –

+2

Prova '1000 è 500 + 500', sarà (probabilmente) essere' Falso'. Vedi, ad esempio, http://stackoverflow.com/questions/306313/python-is-operator-behaves-unexpectedly-with-integers –

risposta

35

un metodo sarebbe quello di utilizzare itertools.islice

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

Non sapevo nemmeno nemmeno della funzione 'next()'. Grazie! –

+3

Sostituisci '5 + 1' con' Nessuno' funziona anche, e legge più semplice –

-1

Forse dovresti approfondire un caso d'uso reale.

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

I editted 'xrange (10)' a '(i for i in xrange (10))'. Risulta che questa sintassi funzioni per 'xrange' poiché non è realmente un generatore ... –

+5

' xrange' precede i generatori e restituisce un oggetto xrange, che implementa effettivamente il protocollo di sequenza completo. –

14

Si potrebbe fare questo, utilizzando count come un generatore ad esempio:

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

Che versione di Python è questa? Il codice precedente mi dà l'errore 'AttributeError: 'l'oggetto' itertools.islice 'non ha attributo' next'' in 3.3. – LarsH

+0

In Python 3x, cambia 'next' in' __next __() ', cioè,' islice (count, n, n = 1) .__ next __() ' – Mohammed

+2

Quindi è meglio usare' next (islice (count(), n , n + 1)) '. –

-2

si può semplicemente convertire il generatore in un elenco e utilizzare l'indice come normale:

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

Contro la tentazione di trattare i generatori come elenchi. L'approccio semplice ma ingenuo è la semplice one-liner:

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

Ma ricordate, i generatori non sono come liste. Non memorizzano i risultati intermedi da nessuna parte, quindi non puoi tornare indietro. Io dimostrare il problema con un semplice esempio nella repl pitone:

>>> 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 

Una volta che si inizia a passare attraverso un generatore per ottenere il valore n-esimo della sequenza, il generatore è ora in uno stato diverso, e il tentativo di ottenere di nuovo l'ennesimo valore ti restituirà un risultato diverso, che probabilmente causerà un bug nel tuo codice.

Diamo un'occhiata a un altro esempio, basato sul codice della domanda.

Si prevede inizialmente quanto segue per stampare 4 due volte.

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) 

ma digitare questo nella repl e si ottiene:

>>> 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 

Buona fortuna tracciamento bug che verso il basso.

0

La prima cosa che mi è venuta in mente è stata:

gen = (i for i in xrange(10)) 
index = 5 

for i, v in zip(range(index), gen): pass 

return v 
Problemi correlati