Resultado extraño al eliminar un elemento de una lista mientras se itera sobre él en Python

Resuelto Finger twist asked hace 13 años • 12 respuestas

Tengo este fragmento de código:

numbers = list(range(1, 50))

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

print(numbers)

Pero el resultado que obtengo 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, espero que los números inferiores a 20 no aparezcan en los resultados. Parece que estoy haciendo algo mal con la eliminación.

Finger twist avatar Jun 07 '11 09:06 Finger twist
Aceptado

Estás modificando la lista mientras la iteras. Eso significa que la primera vez que se realiza el ciclo, i == 1so 1se elimina de la lista. Luego, el forbucle va al segundo elemento de la lista, que no es 2, sino 3! Luego se elimina de la lista y luego el forbucle continúa hasta el tercer elemento de la lista, que ahora es 5. Y así sucesivamente. Quizás sea más fácil visualizarlo así, con ^ apuntando al valor de i:

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

Ese es el estado inicial de la lista; luego 1se elimina y el bucle va al segundo elemento de la lista:

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

Etcétera.

No existe una buena manera de modificar la longitud de una lista mientras se itera sobre ella. Lo mejor que puedes hacer es algo como esto:

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

o esto, para alteración in situ (lo que está entre paréntesis es una expresión generadora, que se convierte implícitamente en una tupla antes de la asignación de sectores):

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

Si desea realizar una operación nantes de quitarlo, un truco que puede 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]
senderle avatar Jun 07 '2011 02:06 senderle

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]
eyquem avatar Jan 06 '2012 02:01 eyquem

¡La respuesta de @senderle es el camino a seguir!

Dicho esto, para ilustrar un poco más tu problema, si lo piensas bien, siempre querrás eliminar el índice 0 veinte veces:

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

Entonces podrías optar por algo como esto:

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.


Las siguientes no son malas prácticas que yo sepa.

EDITAR (algo más):

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

Hará el trabajo también.

EDIT2 (estoy aburrido):

functional = filter(lambda x: x> 20, range(50))
Trufa avatar Jun 07 '2011 03:06 Trufa

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

En primer lugar, crea una matriz de índice, donde enumera todos los índices que desea eliminar, como se muestra a continuación.

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 index_arr. El problema que encontrará es el mismo que antes. Por lo tanto, debe restar 1 de cada índice en index_arr después de eliminar un número del directorio de números, como en el siguiente:

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.

Yassin Julian avatar Nov 14 '2020 15:11 Yassin Julian