¿Cómo invertir una lista? ¿Cómo iterar hacia atrás?

12 minutos de lectura

avatar de usuario
leo.peis

¿Cómo itero sobre una lista al revés en Python?

array = [0, 10, 20, 40]
for (i = array.length() - 1; i >= 0; i--)

  • Posible duplicado de stackoverflow.com/questions/931092/reverse-a-string-in-python

    – Enderman

    24 de junio a las 22:24

  • Por lo que puedo decir, la pregunta solo tenía la intención de ser sobre iteración. Sin embargo, el día después de que se preguntó, alguien trató de corregir el título (bastante pobre) y lo malinterpretó, dándole el título “¿Cómo revertir una lista?” (lo que significa cambiar el orden de los elementos, no mirando los elementos en un orden diferente). Luego, la pregunta atrajo respuestas para ambos problemas, por lo que ahora tenemos un canónico bastante confuso. Al menos agregué al título para indicar que aquí se cumplen ambos propósitos.

    – Karl Knechtel

    el dia de ayer

avatar de usuario
carne_mecanica

>>> xs = [0, 10, 20, 40]
>>> xs[::-1]
[40, 20, 10, 0]

Se explica la sintaxis de segmento extendida aquí. Ver también, documentación.

  • Funciona para cualquier interable, no solo para listas. La desventaja es que no está en su lugar.

    – Suizo

    15 de octubre de 2010 a las 7:04


  • @Tim devuelve un segmento, por lo que no cambia el contenido real de la lista

    – Fortran

    15 de octubre de 2010 a las 7:04


  • @Swiss No en todos los iterables, por ejemplo, set es iterable, pero no subíndice.

    – Fortran

    15 de octubre de 2010 a las 7:06

  • @lunixbochs invertido devuelve un iterador y no una lista en Python 3.

    – Suizo

    15 de octubre de 2010 a las 7:09


  • Esto funciona exactamente para lo que quiere OP, pero es muy complicado, de una manera que tienes que usar exactamente L[::-1] y no tomará valor para start, stop como slice el método se define con. Prueba con L[4:0:-1] y verás el problema.

    – bizi

    28 de enero de 2017 a las 23:06

  • [start:stop:step] entonces el paso es -1

    – papalagi

    26 de septiembre de 2012 a las 3:36

  • Detalle: el primero modifica la lista en el lugar, el segundo simplemente devuelve una nueva lista invertida, pero no modifica la original.

    – franzlorenzon

    29 de octubre de 2013 a las 14:02


  • el segundo ejemplo debe ser L=L[::-1] para revertir la lista; de lo contrario, solo devolverá los valores al revés

    – rjmoggach

    30 de septiembre de 2014 a las 1:53

  • digamos si tengo l= [1,2,3,4,5,6] y n=2 el resultado debe ser [6,5,1,2,3,4]Cómo podemos hacer esto

    – Atul Jain

    24 de enero de 2015 a las 6:56

  • podrías hacer esto: b = l[-n:] b.reverse() l = b + l[:len(l) - n]

    – Shoaib Shakel

    10 de diciembre de 2015 a las 9:41


avatar de usuario
luzcc

Resumen de métodos inversos

Hay tres formas integradas diferentes de invertir una lista. El mejor método depende de si necesita:

  1. Invertir una lista existente en el lugar (alterando la variable de la lista original)
    • La mejor solución es object.reverse() método
  2. Cree un iterador de la lista invertida (porque lo va a enviar a un bucle for, un generador, etc.)
    • La mejor solución es reversed(object) que crea el iterador
  3. Cree una copia de la lista, simplemente en el orden inverso (para conservar la lista original)
    • La mejor solución es usar segmentos con un tamaño de paso de -1: object[::-1]

Desde una perspectiva de velocidad, es mejor usar las funciones integradas anteriores para invertir una lista. Para invertir, son de 2 a 8 veces más rápidos en listas cortas (10 elementos) y hasta ~300 veces más rápidos en listas largas en comparación con un bucle o generador creado manualmente. Esto tiene sentido: están escritos en un idioma nativo (es decir, C), tienen expertos que los crean, controlan y optimizan. También son menos propensos a los defectos y es más probable que manejen casos de borde y esquina.

Guión de prueba

Reúna todos los fragmentos de código de esta respuesta para crear un script que ejecute las diferentes formas de revertir una lista que se describen a continuación. Calculará el tiempo de cada método mientras lo ejecuta 100.000 veces. Los resultados se muestran en la última sección para listas de longitud 2, 10 y 1000 elementos.

from timeit import timeit
from copy import copy

def time_str_ms
    return '{0:8.2f} ms'.format(t * 1000)

Método 1: invertir en el lugar con obj.reverse()

Si el objetivo es simplemente invertir el orden de los elementos en una lista existente, sin pasarlos por encima u obtener una copia con la que trabajar, use el <list>.reverse() función. Ejecute esto directamente en un objeto de lista y el orden de todos los elementos se invertirá:

Tenga en cuenta que lo siguiente invertirá la variable original que se proporciona, aunque también devuelve la lista invertida. es decir, puede crear una copia utilizando esta función de salida. Por lo general, no crearía una función para esto, pero el script de sincronización lo requiere.

Probamos el rendimiento de estas dos formas: primero simplemente invirtiendo una lista en el lugar (cambia la lista original), y luego copiando la lista e invirtiéndola después para ver si esa es la forma más rápida de crear una copia invertida en comparación con la otra. métodos.

def rev_in_place(mylist):
    mylist.reverse()
    return mylist

def rev_copy_reverse(mylist):
    a = copy(mylist)
    a.reverse()
    return a

Método 2: invertir una lista usando sectores obj[::-1]

El método de corte de índice incorporado le permite hacer una copia de parte de cualquier objeto indexado.

  • No afecta al objeto original.
  • Construye una lista completa, no un iterador.

La sintaxis genérica es: <object>[first_index:last_index:step]. Para explotar el corte para crear una lista invertida simple, use: <list>[::-1]. Al dejar una opción vacía, los establece en los valores predeterminados del primer y último elemento del objeto (al revés si el tamaño del paso es negativo).

La indexación permite utilizar números negativos, que cuentan desde el final del índice del objeto hacia atrás (es decir, -2 es el penúltimo elemento). Cuando el tamaño del paso es negativo, comenzará con el último elemento y se indexará hacia atrás en esa cantidad.

def rev_slice(mylist):
    a = mylist[::-1]
    return a

Método 3: invertir una lista con el reversed(obj) función de iterador

Hay un reversed(indexed_object) función:

  • Esto crea un iterador de índice inverso, no una lista. Genial si lo está alimentando a un bucle para un mejor rendimiento en listas grandes
  • Esto crea una copia y no afecta el objeto original.

Pruebe con un iterador sin formato y creando una lista a partir del iterador.

def reversed_iterator(mylist):
    a = reversed(mylist)
    return a

def reversed_with_list(mylist):
    a = list(reversed(mylist))
    return a

Método 4: Lista inversa con indexación personalizada/manual

Como muestra el tiempo, crear sus propios métodos de indexación es una mala idea. Utilice los métodos integrados a menos que realmente necesite hacer algo personalizado. Esto simplemente significa aprender los métodos integrados.

Dicho esto, no hay una gran penalización con tamaños de lista más pequeños, pero cuando aumenta la escala, la penalización se vuelve tremenda. El código a continuación podría optimizarse, estoy seguro, pero nunca puede coincidir con los métodos integrados, ya que se implementan directamente en un idioma nativo.

def rev_manual_pos_gen(mylist):
    max_index = len(mylist) - 1
    return [ mylist[max_index - index] for index in range(len(mylist)) ]

def rev_manual_neg_gen(mylist):
    ## index is 0 to 9, but we need -1 to -10
    return [ mylist[-index-1] for index in range(len(mylist)) ]

def rev_manual_index_loop(mylist):
    a = []
    reverse_index = len(mylist) - 1
    for index in range(len(mylist)):
        a.append(mylist[reverse_index - index])
    return a
    
def rev_manual_loop(mylist):
    a = []
    reverse_index = len(mylist)
    for index, _ in enumerate(mylist):
        reverse_index -= 1
        a.append(mylist[reverse_index])
    return a

Cronometraje de cada método

A continuación se muestra el resto del guión para cronometrar cada método de inversión. Se muestra invirtiendo en el lugar con obj.reverse() y creando el reversed(obj) iterator son siempre los más rápidos, mientras que el uso de segmentos es la forma más rápida de crear una copia.

¡También demuestra que no debes intentar crear una forma de hacerlo por tu cuenta a menos que tengas que hacerlo!

loops_to_test = 100000
number_of_items = 10
list_to_reverse = list(range(number_of_items))
if number_of_items < 15:
    print("a: {}".format(list_to_reverse))
print('Loops: {:,}'.format(loops_to_test))
# List of the functions we want to test with the timer, in print order
fcns = [rev_in_place, reversed_iterator, rev_slice, rev_copy_reverse,
        reversed_with_list, rev_manual_pos_gen, rev_manual_neg_gen,
        rev_manual_index_loop, rev_manual_loop]
max_name_string = max([ len(fcn.__name__) for fcn in fcns ])
for fcn in fcns:
    a = copy(list_to_reverse) # copy to start fresh each loop
    out_str=" | out = {}".format(fcn(a)) if number_of_items < 15 else ''
    # Time in ms for the given # of loops on this fcn
    time_str = time_str_ms(timeit(lambda: fcn(a), number=loops_to_test))
    # Get the output string for this function
    fcn_str="{}(a):".format(fcn.__name__)
    # Add the correct string length to accommodate the maximum fcn name
    format_str="{{fx:{}s}} {{time}}{{rev}}".format(max_name_string + 4)
    print(format_str.format(fx=fcn_str, time=time_str, rev=out_str))

Resultados de tiempo

Los resultados muestran que el escalado funciona mejor con los métodos integrados más adecuados para un tipo particular de inversión. En otras palabras, a medida que aumenta el número de elementos del objeto, los métodos integrados superan aún más a los otros métodos.

El método incorporado que logra directamente lo que necesita es mejor que unir las cosas. es decir, dividir es mejor si necesita una copia de la lista invertida; es más rápido que crear una lista duplicada a partir de list(reversed(obj)) función, y más rápido que hacer una copia de la lista y luego hacer una en el lugar obj.reverse(), pero nunca a más del doble de la velocidad. Mientras tanto, los métodos personalizados pueden tomar órdenes de magnitud más largas con listas grandes.

Para escalar, con una lista de 1000 artículos, el reversed(<list>) la llamada a la función toma ~30 ms para configurar el iterador, la inversión en el lugar toma solo ~55 ms, usar el método de división toma ~210 ms para crear una copia de la lista invertida completa, pero el método manual más rápido que hice tomó ~8400ms.

Con 2 artículos en la lista:

a: [0, 1]
Loops: 100,000
rev_in_place(a):             24.70 ms | out = [1, 0]
reversed_iterator(a):        30.48 ms | out = <list_reverseiterator object at 0x0000020242580408>
rev_slice(a):                31.65 ms | out = [1, 0]
rev_copy_reverse(a):         63.42 ms | out = [1, 0]
reversed_with_list(a):       48.65 ms | out = [1, 0]
rev_manual_pos_gen(a):       98.94 ms | out = [1, 0]
rev_manual_neg_gen(a):       88.11 ms | out = [1, 0]
rev_manual_index_loop(a):    87.23 ms | out = [1, 0]
rev_manual_loop(a):          79.24 ms | out = [1, 0]

Con 10 artículos en la lista:

rev_in_place(a):             23.39 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
reversed_iterator(a):        30.23 ms | out = <list_reverseiterator object at 0x00000290A3CB0388>
rev_slice(a):                36.01 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_copy_reverse(a):         64.67 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
reversed_with_list(a):       50.77 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_pos_gen(a):      162.83 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_neg_gen(a):      167.43 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_index_loop(a):   152.04 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_loop(a):         183.01 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Y con 1000 artículos en la lista:

rev_in_place(a):             56.37 ms
reversed_iterator(a):        30.47 ms
rev_slice(a):               211.42 ms
rev_copy_reverse(a):        295.74 ms
reversed_with_list(a):      418.45 ms
rev_manual_pos_gen(a):     8410.01 ms
rev_manual_neg_gen(a):    11054.84 ms
rev_manual_index_loop(a): 10543.11 ms
rev_manual_loop(a):       15472.66 ms

avatar de usuario
fahad

Para invertir la misma lista, use:

array.reverse()

Para asignar una lista invertida a otra lista, use:

newArray = array[::-1] 

Usando el corte, por ejemplo, array = array[::-1], es un buen truco y muy pitónico, pero tal vez un poco oscuro para los novatos. Usar el método reverse() es una buena manera de comenzar con la codificación diaria porque es fácil de leer.

Sin embargo, si necesita invertir una lista en su lugar como en una pregunta de entrevista, es probable que no pueda utilizar métodos integrados como estos. El entrevistador observará cómo aborda el problema en lugar de la profundidad del conocimiento de Python, se requiere un enfoque algorítmico. El siguiente ejemplo, utilizando un intercambio clásico, podría ser una forma de hacerlo:

def reverse_in_place(lst):      # Declare a function
    size = len(lst)             # Get the length of the sequence
    hiindex = size - 1
    its = size/2                # Number of iterations required
    for i in xrange(0, its):    # i is the low index pointer
        temp = lst[hiindex]     # Perform a classic swap
        lst[hiindex] = lst[i]
        lst[i] = temp
        hiindex -= 1            # Decrement the high index pointer
    print "Done!"

# Now test it!!
array = [2, 5, 8, 9, 12, 19, 25, 27, 32, 60, 65, 1, 7, 24, 124, 654]

print array                    # Print the original sequence
reverse_in_place(array)        # Call the function passing the list
print array                    # Print reversed list


**The result:**
[2, 5, 8, 9, 12, 19, 25, 27, 32, 60, 65, 1, 7, 24, 124, 654]
Done!
[654, 124, 24, 7, 1, 65, 60, 32, 27, 25, 19, 12, 9, 8, 5, 2]

Tenga en cuenta que esto no funcionará en tuplas o secuencias de cadenas, porque las cadenas y las tuplas son inmutables, es decir, no puede escribir en ellas para cambiar elementos.

  • El intercambio clásico podría hacerse a través de lst[hiindex], lst[i] = lst[i], lst[hiindex]Pienso… 😉

    – Samoth

    25 de marzo de 2017 a las 10:50

  • @Samoth Esa sintaxis no es tan clara y el comportamiento no es exactamente obvio. Pasos distintos tienen más sentido.

    – Antonio

    7 julio 2018 a las 18:29

  • por qué la gente dice que cosas como matriz[::-1] son pitónicos? El zen de Python nos enseña que lo explícito es mejor que lo implícito y que la legibilidad cuenta. Cosas como esas no son explícitas ni legibles en absoluto.

    – k4ppa

    20 de agosto de 2018 a las 15:36

  • @k4ppa: array[::-1] es perfectamente legible y perfectamente explícito si conoces python. “Legible” no significa que “alguien que nunca antes haya usado el corte de Python debe poder leerlo”; la [::-1] invertir el segmento es un modismo ridículamente común en Python (lo encontrará en el código existente todo el tiempo), y es perfectamente legible si usas Python regularmente. Por supuesto, first10 = [], for i in range(10): first10.append(array[i]) es claro y explícito, pero eso no lo hace mejor que first10 = array[:10].

    – ShadowRanger

    07/01/2020 a las 18:00


  • ¿Por qué el número de iteraciones es tamaño/2? ¿Cuál es la lógica detrás de esto? Además, ¿qué pasa si su longitud no es un número par? Entonces sería ceil(tamaño/2)

    – Persona anónima

    30 oct 2020 a las 15:39

¿Ha sido útil esta solución?