¿La mejor manera de crear una lista "invertida" en Python?

89

En Python, ¿cuál es la mejor manera de crear una nueva lista cuyos elementos son los mismos que los de alguna otra lista, pero en orden inverso? (No quiero modificar la lista existente en su lugar).

Aquí hay una solución que se me ha ocurrido:

new_list = list(reversed(old_list))

También es posible duplicar y old_listluego invertir el duplicado en su lugar:

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

¿Hay una mejor opción que haya pasado por alto? Si no es así, ¿existe una razón de peso (como la eficiencia) para utilizar uno de los enfoques anteriores sobre el otro?

Davidchambers
fuente

Respuestas:

205
newlist = oldlist[::-1]

los [::-1] corte (que a mi esposa Anna le gusta llamar "el emoticón marciano" ;-) significa: cortar toda la secuencia, con un paso de -1, es decir, al revés. Funciona para todas las secuencias.

Tenga en cuenta que esto ( y las alternativas que mencionó) es equivalente a una "copia superficial", es decir: si los elementos son mutables y llama mutantes en ellos, las mutaciones en los elementos que se encuentran en la lista original también están en los elementos de la lista invertida y viceversa. Si necesita evitar eso, una copy.deepcopy(aunque siempre es una operación potencialmente costosa), seguida en este caso de una .reverse, es la única buena opción.

Alex Martelli
fuente
¡Oh mi! Eso es elegante. Hasta hace unos días no me di cuenta de que es posible incluir un "paso" al cortar; ¡Ahora me pregunto cómo me las arreglé sin él! Gracias, Alex. :)
davidchambers
1
Gracias, también, por mencionar que esto produce una copia superficial. Eso es todo lo que necesito, así que estoy a punto de agregar un emoticón marciano a mi código.
Davidchambers
13
Esto realmente parece mucho más mágico y menos legible que la sugerencia original list(reversed(oldlist)). Aparte de un menor micro-optimización, ¿hay alguna razón para preferir [::-1]a reversed()?
Brian Campbell
@BrianCampbell: ¿Qué tiene de "magia"? Si comprende el corte en rodajas, tiene mucho sentido. Si no comprende el corte ... bueno, debería aprenderlo bastante temprano en su carrera en Python. Por supuesto, reversedtiene una gran ventaja cuando no necesita la lista, porque no desperdicia memoria ni tiempo inicial para construirla. Pero cuando se hace necesario la lista, utilizando [::-1]en lugar de list(reversed())es análogo al uso de un [listcomp]lugar de list(genexpr).
abarnert
10
@abarnert En el código con el que trabajo, rara vez veo el argumento del tercer segmento, si veo un uso de él, tengo que buscar lo que significa. Una vez que lo hago, todavía no es obvio que los valores iniciales y finales predeterminados se intercambien cuando el paso es negativo. De un vistazo rápido, sin buscar el significado del tercer argumento, podría suponer que eso [::-1]significa eliminar el último elemento de una lista, en lugar de invertirlo. reversed(list)declara exactamente lo que está haciendo; enuncia su intención, en el sentido de "lo explícito es mejor que lo implícito", "la legibilidad cuenta" y "escasa es mejor que densa".
Brian Campbell
56

Ahora vamos timeit. Pista: Alex [::-1]es el más rápido :)

$ 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

Actualizar: método de composición de lista agregado sugerido por inspectorG4dget. Dejaré que los resultados hablen por sí mismos.

Sam Dolan
fuente
8
Solo una nota: esto es preciso para crear una lista de copia invertida, pero la inversa es aún más eficiente para la iteración que entonces[::-1]
Tadhg McDonald-Jensen
7

Ajustes

Vale la pena proporcionar un punto de referencia / ajuste de referencia para los cálculos de tiempo de sdolan que muestran el rendimiento de 'invertido' sin la list()conversión a menudo innecesaria . Esta list()operación agrega 26 usecs adicionales al tiempo de ejecución y solo es necesaria en el caso de que un iterador sea inaceptable.

Resultados:

reversed(lst) -- 11.2 usecs

list(reversed(lst)) -- 37.1 usecs

lst[::-1] -- 23.6 usecs

Cálculos:

# 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

Conclusiones:

La conclusión de estas pruebas reversed()es más rápida que la rebanada [::-1]en 12,4 usecs

mekarpeles
fuente
15
reversed () devuelve un objeto iterador que se evalúa de forma diferida, por lo que creo que no es una comparación justa con la notación de corte [:: - 1] en general.
iridiscente
1
Incluso en el caso de que se pueda utilizar un iterador directamente, por ejemplo ''.join(reversed(['1','2','3'])), el método de corte sigue siendo> 30% más rápido.
dansalmo
1
No es de extrañar por qué obtuvo el mismo resultado de las 2 primeras pruebas: ¡son idénticas!
MestreLion
Mis resultados se parecen más a esto. ol [:: - 1] toma aproximadamente el doble de largo que list (invertido (ol)). El método ol [:: - 1] requiere menos caracteres para escribir. Sin embargo, list (reversed (ol)) es probablemente más legible para los programadores principiantes de Python y es más rápido en mi máquina.
dhj