2013-04-19 16 views
5

Qual è il modo corretto di eseguire più iterazioni su un contenitore? Dalla documentazione python:Modo corretto per iterare due volte su un elenco?

Iterator - Un oggetto contenitore (ad esempio un elenco) produce un nuovo e fresco iteratore ogni volta che si passa alla funzione iter() o utilizza in un ciclo for. Se si tenta di eseguire questo con un iteratore, verrà restituito lo stesso oggetto iteratore esaurito utilizzato nel precedente passaggio di iterazione, rendendo visualizzato come un contenitore vuoto.

L'intenzione del protocollo è che una volta che il metodo next() di un iteratore solleva StopIteration, continuerà a farlo nelle chiamate successive. Le implementazioni che non obbediscono a questa proprietà sono considerate non funzionanti. (Questo vincolo è stato aggiunto in Python 2.3; in Python 2.2, vari iteratori sono suddivisi in base a questa regola.)

Se ho questo codice:

slist = [1,2,3,4] 
rlist = reversed(slist) 
list(rlist) 
#[4,3,2,1] 
tuple(rlist) 
#() 

Quale sarebbe la più semplice e il modo più corretto di ripetere su 'rlist' due volte?

+4

Nota che non sei l'iterazione su una lista due volte - È facile. Stai effettivamente iterando su un '' due volte. – mgilson

risposta

7
rlist = list(reversed(slist)) 

Quindi ripetere tutte le volte che si desidera. Questo trucco si applica più in generale; ogni volta che è necessario iterare più volte su un iteratore, trasformarlo in un elenco. Ecco un frammento di codice che tengo copia-incolla in diversi progetti proprio per questo scopo:

def tosequence(it): 
    """Turn iterable into a sequence, avoiding a copy if possible.""" 
    if not isinstance(it, collections.Sequence): 
     it = list(it) 
    return it 

(Sequence è il tipo astratto di liste, tuple e molti oggetti lista simile personalizzati.)

+0

Perché preoccuparsi del test? Mi aspettavo che non ci fosse alcun costo per 'it = list (it)' if 'it' è già una lista. Non è questo il caso? –

5

I wouldn 't memorizzato l'elenco due volte, se non è possibile combinare per scorrere una volta, quindi vorrei

slist = [1,2,3,4] 
for element in reversed(slist): 
    print element # do first iteration stuff 
for element in reversed(slist): 
    print element # do second iteration stuff 

Basti pensare alla invertita() come la creazione di un iteratore inverso su slist. Il contrario è economico. Detto questo, se solo avessi mai bisogno di invertirlo, lo invertirò e lo terrò memorizzato in quel modo.

+1

+1 - Chiamare 'reverseed (slist)' crea solo un nuovo oggetto iteratore che è molto più economico di 'list (reverse (slist))' che crea una copia dell'intera lista. – Aya

3

Qual è il modo corretto di eseguire più iterazioni su un contenitore?

Fallo due volte di seguito. Nessun problema.

Quale sarebbe il modo più semplice e più corretto per scorrere su "rlist" due volte?

Sede, la ragione che non funziona per voi è che rlistnon è "un contenitore".

Notate come

list(slist) # another copy of the list 
tuple(slist) # still works! 

Quindi, la soluzione più semplice è quello di garantire solo si dispone di un contenitore reale di articoli se avete bisogno di iterare più volte:

rlist = list(reversed(slist)) # we store the result of the first iteration 
# and then that result can be iterated over multiple times. 

Se davvero non necessario memorizzare gli articoli, prova itertools.tee. Ma tieni presente che in realtà non eviterete di archiviare gli articoli se è necessario completare una iterazione completa prima di iniziare la successiva. Nel caso generale, lo stoccaggio è davvero inevitabile in base a tali restrizioni.

1

Perché non è sufficiente invertire l'elenco originale sul posto (slist.reverse()), quindi scorrere su di esso tutte le volte che si desidera e infine invertire nuovamente per ottenere l'elenco originale ancora una volta?

Se questo non funziona per voi, la migliore soluzione per iterare sulla lista in ordine inverso è quello di creare un nuovo iteratore inverso ogni volta che è necessario iterare

for _ in xrange(as_many_times_as_i_wish_to_iterate_this_list_in_reverse_order): 
    for x in reversed(slist): 
     do_stuff(x) 
+0

//, perché usi '_' come nome di variabile, qui? È una specie di convenzione? –

+0

I (e ho visto altre persone farlo) assegnano valori throw-away a una variabile di sottolineatura. In questo modo quando leggo il codice saprò immediatamente che questo valore non è rilevante. D'altra parte, la libreria 'gettext' usa una funzione' _() '... quindi suppongo che in alcuni contesti questo sia un brutto nome per una variabile throw-away. –

Problemi correlati