in

¿Cómo puedo revertir una lista en Python?

apple touch icon@2

Hay tres formas integradas diferentes de revertir una lista. Qué método es mejor depende de si necesita:

  1. Invertir una lista existente en el lugar (alterando la variable de lista original)
    • La mejor solución es object.reverse() método
  2. Cree un iterador de la lista invertida (porque lo va a alimentar 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, solo en orden inverso (para preservar la lista original)
    • La mejor solución es usar cortes con un tamaño de paso de -1: object[::-1]

Desde la perspectiva de la velocidad, es mejor utilizar 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ápido en listas largas en comparación con un ciclo o generador creado manualmente. Esto tiene sentido: están escritos en un idioma nativo (es decir, C), tienen expertos que los crean, examinan y optimizan. También son menos propensos a los defectos y es más probable que manejen cajas de bordes y esquinas.

Script de prueba

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

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 su lugar con obj.reverse ()

Si el objetivo es simplemente invertir el orden de los elementos en una lista existente, sin recorrerlos o obtener una copia para 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 la secuencia de comandos de tiempo 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 y revirtié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 división de índices incorporado le permite hacer una copia de parte de cualquier objeto indexado.

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

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

La indexación permite usar 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 por esa cantidad.

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

Método 3: invierta 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 al 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 existe 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 siguiente código 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 medir el tiempo de cada método de inversión. Muestra marcha atrás en su lugar con obj.reverse() y creando el reversed(obj) iterator son siempre los más rápidos, mientras que el uso de porciones es la forma más rápida de crear una copia.

¡También demuestra no intentar crear una forma de hacerlo por su cuenta a menos que sea necesario!

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 recuento de elementos del objeto, los métodos integrados superan aún más a los otros métodos.

El método integrado que logra directamente lo que necesita es mejor que unir 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 obj.reverse(), pero nunca más del doble de la velocidad. Mientras tanto, los métodos personalizados pueden tardar varios órdenes de magnitud más con listas grandes.

Para escalar, con una lista de 1000 elementos, 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, el uso del método de corte demora ~ 210 ms para crear una copia de la lista invertida completa, pero el método manual más rápido que hice tomó ~ 8400 ms.

Con 2 elementos 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 elementos 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 elementos 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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Método python time sleep ()

gfg 200x200 min

Puntero a una matriz | Puntero de matriz