Pregunta ¿Cómo eliminar elementos de una lista al iterar?


Estoy iterando sobre una lista de tuplas en Python, y estoy tratando de eliminarlas si cumplen con ciertos criterios.

for tup in somelist:
    if determine(tup):
         code_to_remove_tup

¿Qué debería usar en lugar de code_to_remove_tup? No puedo entender cómo eliminar el artículo de esta manera.


703
2017-07-30 15:36


origen


Respuestas:


Puede usar una lista de comprensión para crear una nueva lista que contenga solo los elementos que no desea eliminar:

somelist = [x for x in somelist if not determine(x)]

O bien, al asignar a la porción somelist[:], puede mutar la lista existente para que contenga solo los elementos que desea:

somelist[:] = [x for x in somelist if not determine(x)]

Este enfoque podría ser útil si hay otras referencias a somelist que necesita reflejar los cambios.

En lugar de una comprensión, también podrías usar itertools. En Python 2:

from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)

O en Python 3:

from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)

590
2017-07-30 15:41



Las respuestas que sugieren que las listas de comprensión son CASI correctas, excepto que crean una lista completamente nueva y luego le dan el mismo nombre a la lista anterior, ya que NO modifican la lista anterior en su lugar. Eso es diferente de lo que harías por eliminación selectiva, como en la sugerencia de @ Lennart: es más rápido, pero si se accede a tu lista a través de múltiples referencias, el hecho de que estás recolocando una de las referencias y NO alterando el objeto de la lista en sí mismo puede conducir a errores sutiles y desastrosos.

Afortunadamente, es extremadamente fácil obtener la velocidad de comprensión de la lista Y la semántica requerida de la alteración en el lugar, solo código:

somelist[:] = [tup for tup in somelist if determine(tup)]

Tenga en cuenta la sutil diferencia con otras respuestas: esta NO está asignando un nombre simple: está asignando a un segmento de lista que resulta ser la lista completa, reemplazando así la lista contenido  dentro del mismo objeto de la lista de Python, en lugar de simplemente volver a colocar una referencia (del objeto de lista anterior al nuevo objeto de lista) como las otras respuestas.


491
2017-07-30 19:28



Necesita tomar una copia de la lista y repetirla primero, o la iteración fallará con lo que pueden ser resultados inesperados.

Por ejemplo (depende del tipo de lista):

for tup in somelist[:]:
    etc....

Un ejemplo:

>>> somelist = range(10)
>>> for x in somelist:
...     somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]

>>> somelist = range(10)
>>> for x in somelist[:]:
...     somelist.remove(x)
>>> somelist
[]

191
2017-07-30 15:38



for i in xrange(len(somelist) - 1, -1, -1):
    if some_condition(somelist, i):
        del somelist[i]

Tienes que retroceder, de lo contrario, es un poco como aserrar la rama de árbol en la que estás sentado :-)


76
2017-07-30 15:44



Su mejor enfoque para un ejemplo así sería una lista de comprensión

somelist = [tup for tup in somelist if determine(tup)]

En los casos en que haces algo más complejo que llamar a un determine función, prefiero construir una nueva lista y simplemente anexarla a medida que avanzo. Por ejemplo

newlist = []
for tup in somelist:
    # lots of code here, possibly setting things up for calling determine
    if determine(tup):
        newlist.append(tup)
somelist = newlist

Copiando la lista usando remove podría hacer que su código se vea un poco más limpio, como se describe en una de las respuestas a continuación. Definitivamente no debería hacer esto para listas extremadamente grandes, ya que esto implica primero copiar toda la lista, y también realizar una O(n)  remove operación para cada elemento que se elimina, haciendo de esto una O(n^2) algoritmo.

for tup in somelist[:]:
    # lots of code here, possibly setting things up for calling determine
    if determine(tup):
        newlist.append(tup)

45
2017-07-30 15:41



Para aquellos que les gusta la programación funcional:

somelist[:] = filter(lambda tup: not determine(tup), somelist)

o

from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))

35
2017-07-30 15:46



los tutorial oficial de Python 2 4.2. "para declaraciones" dice:

Si necesita modificar la secuencia que está iterando mientras está dentro del ciclo (por ejemplo, para duplicar elementos seleccionados), se recomienda que primero haga una copia. La iteración sobre una secuencia no hace implícitamente una copia. La notación de división lo hace especialmente conveniente:

>>> for w in words[:]:  # Loop over a slice copy of the entire list.
...     if len(w) > 6:
...         words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']

que es lo que se sugirió en: https://stackoverflow.com/a/1207427/895245

los Documentación de Python 2 7.3. "El enunciado for" da el mismo consejo:

Nota: Hay una sutileza cuando la secuencia está siendo modificada por el ciclo (esto solo puede ocurrir para secuencias mutables, es decir listas). Se usa un contador interno para realizar un seguimiento del elemento que se utiliza a continuación, y esto se incrementa en cada iteración. Cuando este contador ha alcanzado la longitud de la secuencia, el ciclo termina. Esto significa que si el conjunto borra el elemento actual (o uno anterior) de la secuencia, se saltará el siguiente elemento (ya que obtiene el índice del elemento actual que ya se ha tratado). Del mismo modo, si el conjunto inserta un elemento en la secuencia anterior al elemento actual, el elemento actual se tratará nuevamente la próxima vez a través del ciclo. Esto puede conducir a errores desagradables que pueden evitarse haciendo una copia temporal usando una porción de la secuencia completa, por ejemplo,

for x in a[:]:
    if x < 0: a.remove(x)

¿Podría Python hacer esto mejor?

Parece que esta API de Python en particular podría mejorarse. Compárelo, por ejemplo, con su contraparte de Java ListIterator, lo que deja en claro que no se puede modificar una lista que se itera excepto con el iterador mismo, y le brinda formas eficientes de hacerlo sin copiar la lista. Vamos, Python!


30
2017-12-12 10:18