Il modo migliore per creare un elenco "invertito" in Python?


89

In Python, qual è il modo migliore per creare un nuovo elenco i cui elementi sono gli stessi di quelli di qualche altro elenco, ma in ordine inverso? (Non voglio modificare l'elenco esistente in posizione.)

Ecco una soluzione che mi è venuta in mente:

new_list = list(reversed(old_list))

È anche possibile duplicare e old_listquindi invertire il duplicato in posizione:

new_list = list(old_list) # or `new_list = old_list[:]`
new_list.reverse()

C'è un'opzione migliore che ho trascurato? In caso negativo, c'è una ragione convincente (come l'efficienza) per utilizzare uno degli approcci di cui sopra rispetto all'altro?

Risposte:


205
newlist = oldlist[::-1]

Lo [::-1]slicing (che a mia moglie Anna piace chiamare "lo smiley marziano" ;-) significa: affetta l'intera sequenza, con un passo di -1, cioè al contrario. Funziona per tutte le sequenze.

Nota che questo ( e le alternative che hai menzionato) è equivalente a una "copia superficiale", cioè: se gli elementi sono mutabili e chiami mutatori su di essi, le mutazioni negli elementi contenuti nella lista originale sono anche negli elementi nella elenco invertito e viceversa. Se è necessario evitarlo , l'unica buona opzione è una copy.deepcopy(sebbene sia sempre un'operazione potenzialmente costosa), seguita in questo caso da a .reverse.


Oh mio! Questo è elegante. Fino a pochi giorni fa non mi rendevo conto che fosse possibile inserire un "passaggio" nell'affettare; ora mi chiedo come ho fatto a cavarmela senza! Grazie, Alex. :)
davidchambers

1
Grazie anche per aver detto che questo produce una copia superficiale. È tutto ciò di cui ho bisogno, quindi sto per aggiungere uno smiley marziano al mio codice.
davidchambers

13
Questo sembra davvero molto più magica e molto meno leggibile il suggerimento originale, list(reversed(oldlist)). Altro che un minore micro-ottimizzazione, non v'è alcun motivo per preferire [::-1]a reversed()?
Brian Campbell

@BrianCampbell: Cosa c'è di "magico" in questo? Se capisci il taglio, ha perfettamente senso. Se non capisci lo slicing ... beh, dovresti davvero impararlo abbastanza presto nella tua carriera in Python. Ovviamente reversedha un enorme vantaggio quando non hai bisogno dell'elenco, perché non spreca memoria o tempo per costruirlo. Ma quando si fa bisogno della lista, utilizzando [::-1]invece di list(reversed())è analogo all'utilizzo di un [listcomp]posto di list(genexpr).
abarnert

10
@abarnert Nel codice con cui lavoro, vedo così raramente il terzo argomento slice, se ne vedo un uso devo cercare cosa significa. Una volta fatto, non è ancora ovvio che i valori di inizio e di fine predefiniti vengano scambiati quando il passaggio è negativo. A una rapida occhiata, senza cercare il significato del terzo argomento, potrei immaginare che [::-1]significhi rimuovere l'ultimo elemento di un elenco, piuttosto che invertirlo. reversed(list)afferma esattamente cosa sta facendo; enuncia il suo intento, nel senso di "Explicit is better than implicit", "Readability counts", e "Sparse is better than dense".
Brian Campbell,

56

Adesso andiamo timeit. Suggerimento: quello di Alex [::-1]è il più veloce :)

$ p -m timeit "ol = [1, 2, 3]; nl = list(reversed(ol))"
100000 loops, best of 3: 2.34 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = list(ol); nl.reverse();"
1000000 loops, best of 3: 0.686 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = ol[::-1];"
1000000 loops, best of 3: 0.569 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = [i for i in reversed(ol)];"
1000000 loops, best of 3: 1.48 usec per loop


$ p -m timeit "ol = [1, 2, 3]*1000; nl = list(reversed(ol))"
10000 loops, best of 3: 44.7 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = list(ol); nl.reverse();"
10000 loops, best of 3: 27.2 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = ol[::-1];"
10000 loops, best of 3: 24.3 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = [i for i in reversed(ol)];"
10000 loops, best of 3: 155 usec per loop

Aggiornamento: aggiunto il metodo di composizione della lista suggerito da inspectorG4dget. Lascerò che i risultati parlino da soli.


8
Solo una nota: questo è accurato per la creazione di un elenco di copie invertito, ma invertito è ancora più efficiente per l'iterazione quindi[::-1]
Tadhg McDonald-Jensen

7

Adeguamenti

Vale la pena fornire un benchmark / aggiustamento di base per i calcoli timeit di sdolan che mostrano le prestazioni di "invertito" senza la list()conversione spesso non necessaria . Questolist() operazione aggiunge altri 26 usec al runtime ed è necessaria solo nel caso in cui un iteratore non sia accettabile.

Risultati:

reversed(lst) -- 11.2 usecs

list(reversed(lst)) -- 37.1 usecs

lst[::-1] -- 23.6 usecs

Calcoli:

# I ran this set of 100000 and came up with 11.2, twice:
python -m timeit "ol = [1, 2, 3]*1000; nl = reversed(ol)"
100000 loops, best of 3: 11.2 usec per loop

# This shows the overhead of list()
python -m timeit "ol = [1, 2, 3]*1000; nl = list(reversed(ol))"
10000 loops, best of 3: 37.1 usec per loop

# This is the result for reverse via -1 step slices
python -m timeit "ol = [1, 2, 3]*1000;nl = ol[::-1]"
10000 loops, best of 3: 23.6 usec per loop

Conclusioni:

La conclusione di questi test è che reversed()è più veloce dello slice [::-1]di 12,4 usec


15
reversed () restituisce un oggetto iteratore che è valutato in modo pigro, quindi penso che non sia un confronto equo con la notazione di taglio [:: - 1] in generale.
iridescente

1
Anche nel caso in cui un iteratore possa essere utilizzato direttamente, ad esempio ''.join(reversed(['1','2','3'])), il metodo slice è ancora> 30% più veloce.
dansalmo

1
Non c'è da stupirsi perché hai ottenuto lo stesso risultato dai primi 2 test: sono identici!
MestreLion

I miei risultati sono più simili a questo. ol [:: - 1] impiega circa il doppio del tempo di list (invertito (ol)). Il metodo ol [:: - 1] richiede meno caratteri per la scrittura. Tuttavia, list (reversed (ol)) è probabilmente più leggibile per i programmatori python principianti ed è più veloce sulla mia macchina.
dhj
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.