una sustituir por otro numero misma lista eliminar elementos elemento comparar como cambiar borrar agregar python iteration

sustituir - eliminar elementos de una lista python



¿Cómo eliminar elementos de una lista mientras se está iterando? (22)

Debe tomar una copia de la lista e iterarla primero, o la iteración fallará con lo que pueden ser resultados inesperados.

Por ejemplo (depende de qué 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 []

Estoy iterando sobre una lista de tuplas en Python, e intento eliminarlas si cumplen con ciertos criterios.

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

¿Qué debo usar en lugar de code_to_remove_tup ? No puedo averiguar cómo quitar el elemento de esta manera.


El método más efectivo es la comprensión de listas, muchas personas muestran su caso, por supuesto, también es una buena manera de obtener un iterator través del filter .

Filter recibe una función y una secuencia. Filter aplica la función pasada a cada elemento, y luego decide si retiene o descarta el elemento dependiendo de si el valor de retorno de la función es True o False .

Hay un ejemplo (obtener las probabilidades en la tupla):

import numpy as np orig_list = np.array([1, 2, 3, 4, 5, 100, 8, 13]) remove_me = [100, 1] cleaned = np.delete(orig_list, remove_me) print(cleaned)

Precaución: Tampoco puedes manejar iteradores. Los iteradores son a veces mejores que las secuencias.


Es posible que desee utilizar el filter() disponible como el incorporado.

Para más detalles ver aquí


Esta respuesta se escribió originalmente en respuesta a una pregunta que desde entonces se ha marcado como duplicada: Eliminación de coordenadas de la lista en python

Hay dos problemas en su código:

1) Cuando usa remove (), intenta eliminar enteros mientras que necesita eliminar una tupla.

2) El bucle for omitirá los elementos de su lista.

Repasemos lo que sucede cuando ejecutamos tu código:

>>> L1 = [(1,2), (5,6), (-1,-2), (1,-2)] >>> for (a,b) in L1: ... if a < 0 or b < 0: ... L1.remove(a,b) ... Traceback (most recent call last): File "<stdin>", line 3, in <module> TypeError: remove() takes exactly one argument (2 given)

El primer problema es que está pasando tanto ''a'' como ''b'' a remove (), pero remove () solo acepta un solo argumento. Entonces, ¿cómo podemos hacer que remove () funcione correctamente con su lista? Necesitamos averiguar qué es cada elemento de tu lista. En este caso, cada uno es una tupla. Para ver esto, accedamos a un elemento de la lista (la indexación comienza en 0):

>>> L1[1] (5, 6) >>> type(L1[1]) <type ''tuple''>

Jajaja Cada elemento de L1 es en realidad una tupla. Así que eso es lo que tenemos que pasar para eliminar (). Las tuplas en python son muy fáciles, se hacen simplemente encerrando valores entre paréntesis. "a, b" no es una tupla, pero "(a, b)" es una tupla. Así que modificamos tu código y lo ejecutamos de nuevo:

# The remove line now includes an extra "()" to make a tuple out of "a,b" L1.remove((a,b))

Este código se ejecuta sin ningún error, pero veamos la lista que genera:

L1 is now: [(1, 2), (5, 6), (1, -2)]

¿Por qué (1, -2) todavía está en tu lista? Resulta que modificar la lista mientras se usa un bucle para recorrerla es una muy mala idea sin un cuidado especial. La razón por la que (1, -2) permanece en la lista es que las ubicaciones de cada elemento dentro de la lista cambiaron entre las iteraciones del bucle for. Veamos lo que sucede si alimentamos el código anterior con una lista más larga:

L1 = [(1,2),(5,6),(-1,-2),(1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)] ### Outputs: L1 is now: [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]

Como puede inferir de ese resultado, cada vez que la sentencia condicional se evalúe como verdadera y se elimine un elemento de la lista, la siguiente iteración del bucle omitirá la evaluación del siguiente elemento en la lista porque sus valores ahora se ubican en diferentes índices.

La solución más intuitiva es copiar la lista, luego iterar sobre la lista original y solo modificar la copia. Puedes intentar hacerlo así:

L2 = L1 for (a,b) in L1: if a < 0 or b < 0 : L2.remove((a,b)) # Now, remove the original copy of L1 and replace with L2 print L2 is L1 del L1 L1 = L2; del L2 print ("L1 is now: ", L1)

Sin embargo, la salida será idéntica a la anterior:

''L1 is now: '', [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]

Esto se debe a que cuando creamos L2, Python no creó realmente un nuevo objeto. En su lugar, simplemente hizo referencia a L2 con el mismo objeto que L1. Podemos verificar esto con ''is'', que es diferente de simplemente "iguales" (==).

>>> L2=L1 >>> L1 is L2 True

Podemos hacer una copia verdadera usando copy.copy (). Entonces todo funciona como se espera:

import copy L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)] L2 = copy.copy(L1) for (a,b) in L1: if a < 0 or b < 0 : L2.remove((a,b)) # Now, remove the original copy of L1 and replace with L2 del L1 L1 = L2; del L2 >>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]

Finalmente, hay una solución más limpia que tener que hacer una copia completamente nueva de L1. La función invertida ():

L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)] for (a,b) in reversed(L1): if a < 0 or b < 0 : L1.remove((a,b)) print ("L1 is now: ", L1) >>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]

Desafortunadamente, no puedo describir adecuadamente cómo funciona la inversión (). Devuelve un objeto ''listreverseiterator'' cuando se le pasa una lista. Por motivos prácticos, puede pensar que crea una copia invertida de su argumento. Esta es la solución que recomiendo.


Las otras respuestas son correctas, ya que generalmente es una mala idea eliminar de una lista que está iterando. La iteración inversa evita los escollos, pero es mucho más difícil seguir el código que lo hace, por lo que normalmente es mejor usar una lista de comprensión o filter .

Sin embargo, hay un caso en el que es seguro eliminar elementos de una secuencia que está iterando: si solo está eliminando un elemento mientras está iterando. Esto se puede garantizar mediante una return o un break . Por ejemplo:

for i, item in enumerate(lst): if item % 4 == 0: foo(item) del lst[i] break

A menudo, esto es más fácil de entender que una comprensión de lista cuando realiza algunas operaciones con efectos secundarios en el primer elemento de una lista que cumple con alguna condición y luego lo elimina de la lista inmediatamente después.


Las respuestas que sugieren que las listas de comprensión son CASI correctas, excepto que crean una lista completamente nueva y luego le asignan el mismo nombre a la lista anterior, NO modifican la lista anterior en su lugar. Eso es diferente de lo que estaría haciendo al eliminarlo de manera selectiva, como lo sugiere la sugerencia de @Lennart: es más rápido, pero si se accede a su lista a través de varias referencias, el hecho de que solo esté redistribuyendo una de las referencias y NO altere el objeto de la lista en sí mismo puede conducir a errores sutiles, desastrosos.

Afortunadamente, es extremadamente fácil obtener tanto la velocidad de las comprensiones de la lista como la semántica requerida de la alteración in situ, solo el código:

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

Tenga en cuenta la sutil diferencia con otras respuestas: esta NO se asigna a un nombre de barra: se asigna a una porción de lista que resulta ser la lista completa, por lo que se reemplaza el contenido de la lista dentro del mismo objeto de 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.


Necesitaba hacer algo similar y, en mi caso, el problema era la memoria: necesitaba unir múltiples objetos de conjunto de datos dentro de una lista, después de hacer algunas cosas con ellos, como un nuevo objeto, y necesitaba deshacerme de cada entrada a la que me estaba fusionando. Evita duplicarlas todas y hacer explotar la memoria. En mi caso, tener los objetos en un diccionario en lugar de una lista funcionó bien:

`` `

k = range(5) v = [''a'',''b'',''c'',''d'',''e''] d = {key:val for key,val in zip(k, v)} print d for i in range(5): print d[i] d.pop(i) print d

`` `


Necesitaba hacer esto con una lista enorme, y duplicar la lista me pareció costoso, especialmente porque en mi caso la cantidad de eliminaciones sería muy baja en comparación con los elementos que quedan. Tomé este enfoque de bajo nivel.

array = [lots of stuff] arraySize = len(array) i = 0 while i < arraySize: if someTest(array[i]): del array[i] arraySize -= 1 else: i += 1

Lo que no sé es qué tan eficientes son un par de eliminaciones en comparación con copiar una lista grande. Por favor comenta si tienes alguna idea.


Para los 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))


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 lista somelist[:] , puede mutar la lista existente para que contenga solo los elementos que desee:

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

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

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

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

O en Python 3:

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


Puedes probar el bucle inverso, por lo que para some_list harás algo como:

list_len = len(some_list) for i in range(list_len): reverse_i = list_len - 1 - i cur = some_list[reverse_i] # some logic with cur element if some_condition: some_list.pop(reverse_i)

De esta manera, el índice se alinea y no sufre las actualizaciones de la lista (independientemente de si se abre o no el elemento).


Si desea hacer algo más durante la iteración, puede ser bueno obtener tanto el índice (lo que garantiza que pueda hacer referencia a él, por ejemplo, si tiene una lista de dictados) como el contenido real del elemento de la lista.

inlist = [{''field1'':10, ''field2'':20}, {''field1'':30, ''field2'':15}] for idx, i in enumerate(inlist): do some stuff with i[''field1''] if somecondition: xlist.append(idx) for i in reversed(xlist): del inlist[i]

enumerate le da acceso al elemento y al índice a la vez. reversed es para que los índices que luego elimines no cambien en ti.


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

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

En los casos en los que estás haciendo algo más complejo que llamar a una función de determine , prefiero construir una nueva lista y simplemente agregarla 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

Copiar la lista usando remove puede 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 debe hacer esto para listas extremadamente grandes, ya que esto implica copiar primero la lista completa, y también realizar una operación de remove O(n) para cada elemento que se elimina, haciendo de este un algoritmo O(n^2) .

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


TLDR:

Escribí una biblioteca que te permite hacer esto:

from fluidIter import FluidIterable fSomeList = FluidIterable(someList) for tup in fSomeList: if determine(tup): # remove ''tup'' without "breaking" the iteration fSomeList.remove(tup) # tup has also been removed from ''someList'' # as well as ''fSomeList''

Si es posible, es mejor usar otro método que no requiera modificar su iterable mientras se itera sobre él, pero para algunos algoritmos puede que no sea tan sencillo. Y si está seguro de que realmente desea el patrón de código descrito en la pregunta original, es posible.

Debería funcionar en todas las secuencias mutables no solo en listas.

Respuesta completa:

Edición: el último ejemplo de código en esta respuesta proporciona un caso de uso por el que a veces es posible que desee modificar una lista en lugar de usar una lista de comprensión. La primera parte de las respuestas sirve como tutorial de cómo se puede modificar una matriz en su lugar.

La solución se deduce de this respuesta (para una pregunta relacionada) del remitente. Lo que explica cómo se actualiza el índice de matriz al iterar a través de una lista que se ha modificado. La solución a continuación está diseñada para rastrear correctamente el índice de matriz, incluso si se modifica la lista.

Descargue fluidIter.py desde here https://github.com/alanbacon/FluidIterator , es solo un archivo, por lo que no es necesario instalar git. No hay un instalador, por lo que deberá asegurarse de que el archivo se encuentre en la ruta de Python. El código se ha escrito para python 3 y no se ha probado en python 2.

from fluidIter import FluidIterable l = [0,1,2,3,4,5,6,7,8] fluidL = FluidIterable(l) for i in fluidL: print(''initial state of list on this iteration: '' + str(fluidL)) print(''current iteration value: '' + str(i)) print(''popped value: '' + str(fluidL.pop(2))) print('' '') print(''Final List Value: '' + str(l))

Esto producirá la siguiente salida:

initial state of list on this iteration: [0, 1, 2, 3, 4, 5, 6, 7, 8] current iteration value: 0 popped value: 2 initial state of list on this iteration: [0, 1, 3, 4, 5, 6, 7, 8] current iteration value: 1 popped value: 3 initial state of list on this iteration: [0, 1, 4, 5, 6, 7, 8] current iteration value: 4 popped value: 4 initial state of list on this iteration: [0, 1, 5, 6, 7, 8] current iteration value: 5 popped value: 5 initial state of list on this iteration: [0, 1, 6, 7, 8] current iteration value: 6 popped value: 6 initial state of list on this iteration: [0, 1, 7, 8] current iteration value: 7 popped value: 7 initial state of list on this iteration: [0, 1, 8] current iteration value: 8 popped value: 8 Final List Value: [0, 1]

Anteriormente hemos utilizado el método pop en el objeto de lista fluida. También se implementan otros métodos comunes iterables como del fluidL[i] , .remove , .insert , .append , .extend . La lista también se puede modificar utilizando segmentos (no se implementan los métodos de sort e reverse ).

La única condición es que solo debe modificar la lista en su lugar, si en algún punto fluidL o l fue reasignado a un objeto de lista diferente, el código no funcionaría. El bucle for aún fluidL objeto fluidL original, pero quedaría fuera del alcance para que lo modifiquemos.

es decir

fluidL[2] = ''a'' # is OK fluidL = [0, 1, ''a'', 3, 4, 5, 6, 7, 8] # is not OK

Si queremos acceder al valor de índice actual de la lista, no podemos utilizar enumerar, ya que esto solo cuenta cuántas veces se ha ejecutado el bucle for. En su lugar usaremos el objeto iterador directamente.

fluidArr = FluidIterable([0,1,2,3]) # get iterator first so can query the current index fluidArrIter = fluidArr.__iter__() for i, v in enumerate(fluidArrIter): print(''enum: '', i) print(''current val: '', v) print(''current ind: '', fluidArrIter.currentIndex) print(fluidArr) fluidArr.insert(0,''a'') print('' '') print(''Final List Value: '' + str(fluidArr))

Esto dará salida a lo siguiente:

enum: 0 current val: 0 current ind: 0 [0, 1, 2, 3] enum: 1 current val: 1 current ind: 2 [''a'', 0, 1, 2, 3] enum: 2 current val: 2 current ind: 4 [''a'', ''a'', 0, 1, 2, 3] enum: 3 current val: 3 current ind: 6 [''a'', ''a'', ''a'', 0, 1, 2, 3] Final List Value: [''a'', ''a'', ''a'', ''a'', 0, 1, 2, 3]

La clase FluidIterable solo proporciona una envoltura para el objeto de lista original. Se puede acceder al objeto original como una propiedad del objeto fluido de la siguiente manera:

originalList = fluidArr.fixedIterable

Se pueden encontrar más ejemplos / pruebas en la sección if __name__ is "__main__": en la parte inferior de fluidIter.py . Vale la pena mirarlos porque explican lo que sucede en diversas situaciones. Tales como: Reemplazar grandes secciones de la lista usando un sector. O usando (y modificando) el mismo iterable en anidados para bucles.

Como dije para comenzar: esta es una solución complicada que perjudicará la legibilidad de su código y hará que sea más difícil de depurar. Por lo tanto, otras soluciones como la lista de comprensión mencionada en la answer David Raznick deben considerarse primero. Dicho esto, he encontrado momentos en los que esta clase me ha sido útil y me ha resultado más fácil de usar que hacer un seguimiento de los índices de los elementos que deben eliminarse.

Edit: Como se mencionó en los comentarios, esta respuesta no presenta realmente un problema para el cual este enfoque proporciona una solución. Intentaré abordar esto aquí:

Las comprensiones de listas proporcionan una forma de generar una nueva lista, pero estos enfoques tienden a considerar cada elemento de forma aislada en lugar del estado actual de la lista en su conjunto.

es decir

newList = [i for i in oldList if testFunc(i)]

Pero, ¿qué testFunc si el resultado de testFunc depende de los elementos que ya se han agregado a newList ? ¿O los elementos que todavía están en oldList que podrían agregarse a continuación? Puede que aún haya una forma de utilizar una lista de comprensión, pero comenzará a perder su elegancia, y para mí es más fácil modificar una lista en su lugar.

El siguiente código es un ejemplo de un algoritmo que sufre el problema anterior. El algoritmo reducirá una lista para que ningún elemento sea un múltiplo de cualquier otro elemento.

randInts = [70, 20, 61, 80, 54, 18, 7, 18, 55, 9] fRandInts = FluidIterable(randInts) fRandIntsIter = fRandInts.__iter__() # for each value in the list (outer loop) # test against every other value in the list (inner loop) for i in fRandIntsIter: print('' '') print(''outer val: '', i) innerIntsIter = fRandInts.__iter__() for j in innerIntsIter: innerIndex = innerIntsIter.currentIndex # skip the element that the outloop is currently on # because we don''t want to test a value against itself if not innerIndex == fRandIntsIter.currentIndex: # if the test element, j, is a multiple # of the reference element, i, then remove ''j'' if j%i == 0: print(''remove val: '', j) # remove element in place, without breaking the # iteration of either loop del fRandInts[innerIndex] # end if multiple, then remove # end if not the same value as outer loop # end inner loop # end outerloop print('''') print(''final list: '', randInts)

La salida y la lista reducida final se muestran a continuación

outer val: 70 outer val: 20 remove val: 80 outer val: 61 outer val: 54 outer val: 18 remove val: 54 remove val: 18 outer val: 7 remove val: 70 outer val: 55 outer val: 9 remove val: 18 final list: [20, 61, 7, 55, 9]


También podría ser inteligente crear una nueva lista si el elemento de la lista actual cumple con los criterios deseados.

asi que:

for item in originalList: if (item != badValue): newList.append(item)

y para evitar tener que volver a codificar todo el proyecto con el nuevo nombre de las listas:

originalList[:] = newList

nota, de la documentación de Python:

copy.copy (x) Devuelve una copia superficial de x.

copy.deepcopy (x) Devuelve una copia profunda de x.


Una posible solución, útil si desea no solo eliminar algunas cosas, sino también hacer algo con todos los elementos en un solo bucle:

alist = [''good'', ''bad'', ''good'', ''bad'', ''good''] i = 0 for x in alist[:]: if x == ''bad'': alist.pop(i) i -= 1 # do something cool with x or just print x print(x) i += 1


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

Si necesita modificar la secuencia sobre la que está iterando mientras se encuentra dentro del bucle (por ejemplo, para duplicar elementos seleccionados), se recomienda que primero haga una copia. Iterar sobre una secuencia no hace una copia implícitamente. La notación de corte hace que esto sea 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://.com/a/1207427/895245

La documentación de Python 2 7.3. "La declaración for" da el mismo consejo :

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

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

¿Python podría hacer esto mejor?

Parece que esta API de Python en particular podría mejorarse. Compárelo, por ejemplo, con su homólogo de lista ListIterator , que deja en claro que no puede modificar una lista que se está iterando excepto con el propio iterador, y le brinda formas eficientes de hacerlo sin copiar la lista. ¡Vamos, Python!


En algunas situaciones, donde está haciendo más que simplemente filtrar una lista un elemento a la vez, desea que su iteración cambie mientras se realiza la iteración.

Aquí hay un ejemplo en el que copiar la lista de antemano es incorrecto, la iteración inversa es imposible y una comprensión de la lista tampoco es una opción.

i = 0 while i < len(somelist): if determine(somelist[i]): del somelist[i] else: i += 1


Inmediatamente desea crear una copia de la lista para poder tenerla como referencia cuando esté iterando y eliminando tuplas en esa lista que cumplan con ciertos criterios.

Luego, depende del tipo de lista que desee para la salida, ya sea una lista de las tuplas eliminadas o una lista de las tuplas que no se eliminan.

Como señaló David, recomiendo la comprensión de la lista para mantener los elementos que no desea eliminar.

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


Para cualquier cosa que tenga el potencial de ser realmente grande, uso lo siguiente.

""" Sieve of Eratosthenes """ def generate_primes(n): """ Generates all primes less than n. """ primes = list(range(2,n)) idx = 0 while idx < len(primes): p = primes[idx] for multiple in range(p+p, n, p): try: primes.remove(multiple) except ValueError: pass #EAFP idx += 1 yield p

Eso debería ser significativamente más rápido que cualquier otra cosa.


La mayoría de las respuestas aquí quieren que crees una copia de la lista. Tuve un caso de uso donde la lista era bastante larga (110K artículos) y era más inteligente seguir reduciendo la lista.

En primer lugar, deberás reemplazar el bucle foreach por el bucle while ,

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

El valor de ino se cambia en el bloque if porque querrá obtener el valor del nuevo elemento DESDE EL MISMO ÍNDICE, una vez que se elimine el elemento anterior.


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

Tienes que ir hacia atrás, de lo contrario es un poco como cortar la rama de un árbol en la que estás sentado :-)

Usuarios de Python 2: reemplace el range por xrange para evitar crear una lista codificada