Resultado extraño al eliminar el elemento de una lista mientras se iteraba en Python

Tengo este código:

numbers = list(range(1, 50))

for i in numbers:
    if i < 20:
        numbers.remove(i)

print(numbers)

Pero el resultado que estoy recibiendo es:
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]

Por supuesto, estoy esperando que los números inferiores a 20 no aparezcan en los resultados. Parece que estoy haciendo algo malo con la retirada.

Pregunta hecha hace 12 años, 8 meses, 23 días - Por rubyrogue


11 Respuestas:

  • Estás modificando la lista mientras lo haces. Eso significa que la primera vez a través del bucle, i == 1, entonces 1 se elimina de la lista. Entonces el for el bucle va al segundo artículo de la lista, que no es 2, pero 3! Entonces eso es eliminado de la lista, y luego el for el bucle pasa al tercer punto de la lista, que es ahora 5. Y así. Tal vez es más fácil visualizar así, con un ^ apuntando al valor de i:

    [1, 2, 3, 4, 5, 6...]
     ^
    

    Ese es el estado de la lista inicialmente; entonces 1 se retira y el bucle va al segundo artículo de la lista:

    [2, 3, 4, 5, 6...]
        ^
    [2, 4, 5, 6...]
           ^
    

    Y así.

    No hay buena manera de alterar la longitud de una lista mientras se gira sobre ella. Lo mejor que puedes hacer es algo así:

    numbers = [n for n in numbers if n >= 20]
    

    o esto, para la alteración en el lugar (la cosa en parens es una expresión del generador, que se convierte implícitamente en un tuple antes de la asignación de rebanadas):

    numbers[:] = (n for n in numbers if n >= 20)
    

    Si desea realizar una operación en n antes de quitarlo, un truco que podrías probar es este:

    for i, n in enumerate(numbers):
        if n < 20:
            print("do something")
            numbers[i] = None
    numbers = [n for n in numbers if n is not None]
    

    Respondida el Jun 07, 2011 a las 02:35 - por compilerchieftain

    Votos positivos: 0 | Votos negativos: 0

  • Comience al final de la lista y retroceda:

    li = list(range(1, 15))
    print(li)
    
    for i in range(len(li) - 1, -1, -1):
        if li[i] < 6:
            del li[i]
            
    print(li)
    

    Resultado:

    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] 
    [6, 7, 8, 9, 10, 11, 12, 13, 14]
    

    Respondida el Jun 07, 2011 a las 02:45 - por compilerhero

    Votos positivos: 0 | Votos negativos: 0

  • @senderle's ¡La respuesta es la manera de ir!

    Habiendo dicho que para ilustrar aún más su problema, si lo piensas, siempre querrás eliminar el índice 0 veinte veces:

    [1,2,3,4,5............50]
     ^
    [2,3,4,5............50]
     ^
    [3,4,5............50]
     ^
    

    Así que podrías ir con algo así:

    aList = list(range(50))
    i = 0
    while i < 20:
        aList.pop(0)
        i += 1
    
    print(aList) #[21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
    

    Espero que ayude.


    Los siguientes son: no malas prácticas AFAIK.

    EDIT (Algunos más):

    lis = range(50)
    lis = lis[20:]
    

    También hará el trabajo.

    EDIT2 (Estoy aburrido):

    functional = filter(lambda x: x> 20, range(50))
    

    Respondida el Jun 07, 2011 a las 02:53 - por bugbusterx

    Votos positivos: 0 | Votos negativos: 0

  • Así que encontré una solución pero es muy torpe...

    En primer lugar usted hace un array índice, donde usted enumera todo el índice' que desea eliminar como en los siguientes

    numbers = range(1, 50)
    index_arr = []
    
    for i in range(len(numbers):
        if numbers[i] < 20:
            index_arr.append(i)
    
    

    después de eso desea eliminar todas las entradas de la lista de números con el índice guardado en el index_arr. El problema que encontrará es el mismo que antes. Por lo tanto usted tiene que restar 1 de cada índice en el index_arr después de que usted acaba de quitar un número de los números arr, como en los siguientes:

    numbers = range(1, 50)
    index_arr = []
    
    for i in range(len(numbers):
        if numbers[i] < 20:
            index_arr.append(i)
    
    for del_index in index_list:
        numbers.pop(del_index)
    
        #the nasty part
        for i in range(len(index_list)):
            index_list[i] -= 1
    

    Funcionará, pero supongo que no es la forma prevista de hacerlo.

    Respondida el Jun 07, 2011 a las 03:03 - por byteninja8245

    Votos positivos: 0 | Votos negativos: 0

  • Como información adicional a la respuesta de @Senderle, sólo para los registros, pensé que es útil visualizar la lógica detrás de la escena cuando Python ve for en unaTipo de secuencia".

    Digamos que tenemos:

    lst = [1, 2, 3, 4, 5]
    
    for i in lst:
        print(i ** 2)
    

    En realidad va a ser:

    index = 0
    while True:
        try:
            i = lst.__getitem__(index)
        except IndexError:
            break
        print(i ** 2)
        index += 1
    

    Eso es lo que es, hay un mecanismo de búsqueda de pruebas que for tiene cuando lo utilizamos en un tipo de secuencia o Iterables (Es un poco diferente aunque - llamando next() y StopIteration Excepción).

    *Lo único que intento decir es que Python mantendrá un seguimiento de una variable independiente aquí llamada index, así que no importa lo que pase a la lista (removiendo o añadiendo), aumentos de pitón que variable y llamadas __getitem__() método con "esta variable" y pide artículo.

    Respondida el Jun 07, 2011 a las 03:12 - por codemaestro

    Votos positivos: 0 | Votos negativos: 0

  • Construyendo y simplemente la respuesta de @eyquem ...

    El problema es que los elementos están siendo arrancados de debajo de usted mientras usted iterate, saltando números a medida que avanza a lo que era el próximo número.

    Si empiezas desde el final y retrocedes, eliminar los elementos en el camino no importará, porque cuando pasa al elemento "nexto" (en realidad el elemento anterior), la eliminación no afecta a la primera mitad de la lista.

    Simplemente añadir reversed() a su iterador resuelve el problema. Un comentario sería buena forma para evitar que los futuros desarrolladores "tidying up" su código y romperlo misteriosamente.

    for i in reversed(numbers): # `reversed` so removing doesn't foobar iteration
      if i < 20:
        numbers.remove(i)
    

    Respondida el Jun 07, 2011 a las 03:17 - por debugdragon

    Votos positivos: 0 | Votos negativos: 0

  • También podría usarse para continuar ignorar los valores menos de 20

    mylist = []
    
    for i in range(51):
        if i<20:
            continue
        else:
            mylist.append(i)
    print(mylist)
    

    Respondida el Jun 07, 2011 a las 03:24 - por techmaestro

    Votos positivos: 0 | Votos negativos: 0

  • Desde Python 3.3 puede utilizar la lista copy() método como el iterador:

    numbers = list(range(1, 50))
    
    for i in numbers.copy():
        if i < 20:
            numbers.remove(i)
    print(numbers)
    
    [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
    

    Respondida el Jun 07, 2011 a las 03:30 - por quantumquill

    Votos positivos: 0 | Votos negativos: 0

  • Puedes usar list() para numbers crear una copia diferente numbers como se muestra a continuación:

    numbers = list(range(1, 50))
           # ↓ ↓ Here ↓ ↓
    for i in list(numbers):
        if i < 20:
            numbers.remove(i)
    
    print(numbers) # [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 
                   #  31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 
                   #  f42, 43, 44, 45, 46, 47, 48, 49]
    

    Respondida el Jun 07, 2011 a las 03:39 - por devdynamox

    Votos positivos: 0 | Votos negativos: 0

  • El problema que estás experimentando es causado por el hecho de que estás modificando la lista numbers mientras la estás recorriendo con un bucle for. Esto puede llevar a resultados inesperados, ya que al eliminar elementos mientras se itera sobre la lista, se altera la estructura subyacente sobre la que se está iterando.

    Una forma de solucionar esto es crear una nueva lista que contenga los elementos que deseas mantener, en lugar de eliminar los elementos de la lista original. Aquí te muestro cómo podrías modificar tu código para lograr esto:

    python
    numbers = list(range(1, 50))
    new_numbers = []
    
    for i in numbers:
        if i >= 20:
            new_numbers.append(i)
    
    print(new_numbers)
    

    En este código, estamos iterando sobre la lista numbers, y si el elemento es mayor o igual a 20, lo agregamos a la lista new_numbers. Al final, new_numbers contendrá solo los elementos que son mayores o iguales a 20, lo que da como resultado [20, 21, 22, ..., 49] en lugar de mantener los elementos menores a 20

    Respondida el Jun 08, 2011 a las 02:39 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • El problema en tu código surge al eliminar elementos de la lista numbers mientras iteras sobre ella. Cuando eliminas un elemento de una lista durante la iteración, el índice del elemento actual cambia, lo que lleva a resultados inesperados.

    Para resolver este problema, puedes utilizar un enfoque diferente, como crear una nueva lista que contenga los números que deseas conservar:

    python
    numbers = list(range(1, 50))
    filtered_numbers = []
    
    for i in numbers:
        if i >= 20:
            filtered_numbers.append(i)
    
    

    Imprime la lista filtrada

    print(filtered_numbers)

    Este código itera sobre la lista numbers y agrega solo los números mayores o iguales a 20 a la nueva lista filtered_numbers. Una vez que se completa la iteración, filtered_numbers contendrá los números deseados.

    Respondida el Jun 08, 2011 a las 02:41 - por Gemini

    Votos positivos: 0 | Votos negativos: 0