Resultado extraño al eliminar un elemento de una lista mientras se itera sobre él en Python
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.
Estás modificando la lista mientras la iteras. Eso significa que la primera vez que se realiza el ciclo, i == 1
so 1
se elimina de la lista. Luego, el for
bucle va al segundo elemento de la lista, que no es 2
, sino 3
! Luego se elimina de la lista y luego el for
bucle 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 1
se 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 n
antes 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]
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]
¡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))
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.