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 entender cómo quitar el artículo de esta manera.
Respuestas:
Puede usar una comprensión de la lista para crear una nueva lista que contenga solo los elementos que no desea eliminar:
O, al asignar al segmento
somelist[:]
, puede mutar la lista existente para que contenga solo los elementos que desee:Este enfoque podría ser útil si hay otras referencias
somelist
que necesiten reflejar los cambios.En lugar de una comprensión, también podría usar
itertools
. En Python 2:O en Python 3:
En aras de la claridad y para aquellos que encuentran el uso de la
[:]
notación hackish o difusa, aquí hay una alternativa más explícita. Teóricamente, debería realizar lo mismo con respecto al espacio y el tiempo que las frases anteriores.También funciona en otros idiomas que pueden no tener la capacidad de reemplazar elementos de las listas de Python, con modificaciones mínimas. Por ejemplo, no todos los idiomas lanzan listas vacías a a
False
como lo hace Python. Puede sustituirwhile somelist:
por algo más explícito comowhile len(somelist) > 0:
.fuente
somelist[:] = (x for x in somelist if determine(x))
esto creará un generador que puede no crear copias innecesarias.list_ass_slice()
función que implementasomelist[:]=
llamadasPySequence_Fast()
internamente. Esta función siempre devuelve una lista, es decir, la solución de @Alex Martelli que ya utiliza una lista en lugar de un generador es muy probablemente más eficientesomelist
estaría mutada la lista original en ambos métodos?Las respuestas que sugieren la comprensión de la lista son casi correctas, excepto que construyen una lista completamente nueva y luego le dan el mismo nombre que la lista anterior, ya que NO modifican la lista anterior en su lugar. Eso es diferente de lo que estaría haciendo mediante la eliminación selectiva, como en la sugerencia de @ Lennart : es más rápido, pero si se accede a su lista a través de múltiples referencias, el hecho de que simplemente está volviendo a colocar una de las referencias y NO está alterando el objeto de la lista en sí mismo puede conducir a errores sutiles y desastrosos.
Afortunadamente, es extremadamente fácil obtener tanto la velocidad de la comprensión de la lista como la semántica requerida de la alteración en el lugar: solo codifique:
Tenga en cuenta la sutil diferencia con otras respuestas: esta NO se está asignando a un nombre desnudo: se está asignando a un segmento de lista que resulta ser la lista completa, reemplazando así el contenido de la lista dentro del mismo objeto de lista de Python , en lugar de simplemente restablecer una referencia (desde el objeto de lista anterior al nuevo objeto de lista) como las otras respuestas.
fuente
a
por el contenido de dictb
, usea.clear(); a.update(b)
.x = ['foo','bar','baz']; y = x; x = [item for item in x if determine(item)];
Esto reasignax
al resultado de la comprensión de la lista, peroy
aún se refiere a la lista original['foo','bar','baz']
. Si esperabax
y hacey
referencia a la misma lista, es posible que haya introducido errores. A prevenir esto asignando a una rebanada de toda la lista, como Alex espectáculos, y que muestro aquí:x = ["foo","bar","baz"]; y = x; x[:] = [item for item in x if determine(item)];
. La lista se modifica en su lugar. asegurando que todas las referencias a la lista (ambasx
yy
aquí) hagan referencia a la nueva lista.filter
función también crea una nueva lista, no modifica los elementos en su lugar ... soloolist[:] = [i for i in olist if not dislike(i)]
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):
Un ejemplo:
fuente
list(somelist)
convertirá un iterable en una lista.somelist[:]
realiza una copia de un objeto que admite el corte. Por lo tanto, no necesariamente hacen lo mismo. En este caso quiero hacer una copia delsomelist
objeto, así que uso[:]
remove()
tiene que ir a TODA la lista para cada iteración, por lo que tomará una eternidad.Debe ir hacia atrás, de lo contrario, es un poco como cortar la rama del árbol en la que está sentado :-)
Usuarios de Python 2: reemplace
range
porxrange
para evitar crear una lista codificadafuente
reversed()
orden internaenumerate
devuelve un iterador yreversed
espera una secuencia. Supongo que podría hacerloreversed(list(enumerate(somelist)))
si no le importa crear una lista adicional en la memoria.m
más lento.Tutorial oficial de Python 2 4.2. "para declaraciones"
https://docs.python.org/2/tutorial/controlflow.html#for-statements
Esta parte de los documentos deja en claro que:
[:]
Documentación de Python 2 7.3. "El para declaración"
https://docs.python.org/2/reference/compound_stmts.html#for
Esta parte de los documentos dice una vez más que debe hacer una copia y proporciona un ejemplo de eliminación real:
Sin embargo, no estoy de acuerdo con esta implementación, ya que
.remove()
tiene que iterar toda la lista para encontrar el valor.Las mejores soluciones
Ya sea:
iniciar una nueva matriz desde cero y
.append()
volver al final: https://stackoverflow.com/a/1207460/895245Esta vez eficiente, pero menos eficiente en espacio porque mantiene una copia de la matriz durante la iteración.
utilizar
del
con un índice: https://stackoverflow.com/a/1207485/895245Esto es más eficiente en el espacio ya que distribuye la copia de la matriz, pero es menos eficiente en el tiempo porque las listas de CPython se implementan con matrices dinámicas .
Esto significa que la eliminación de elementos requiere cambiar todos los elementos siguientes por uno, que es O (N).
En general, solo desea ir a la
.append()
opción más rápida de forma predeterminada a menos que la memoria sea una gran preocupación.¿Podría Python hacer esto mejor?
Parece que esta API de Python en particular podría mejorarse. Compárelo, por ejemplo, con:
std::vector::erase
que devuelve un interador válido al elemento después del eliminadoambos dejan en claro que no puede modificar una lista que se está iterando, excepto con el iterador mismo, y le brinda formas eficientes de hacerlo sin copiar la lista.
Tal vez la razón subyacente es que las listas de Python se supone que son matriz dinámica respaldado, y por lo tanto cualquier tipo de eliminación serán tiempo ineficiente de todos modos, mientras que Java tiene una jerarquía interfaz mejor con ambos
ArrayList
yLinkedList
las implementaciones deListIterator
.Tampoco parece haber un tipo de lista vinculada explícita en Python stdlib: Lista vinculada de Python
fuente
Su mejor enfoque para tal ejemplo sería una lista de comprensión
En los casos en que está haciendo algo más complejo que llamar a una
determine
función, prefiero construir una nueva lista y simplemente agregarla a medida que avanzo. Por ejemploCopiar 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 unaO(n)
remove
operación para cada elemento que se elimina, lo que lo convierte en unO(n^2)
algoritmo.fuente
Para aquellos que les gusta la programación funcional:
o
fuente
filter
, y más Pythonic. 2. Si necesitalambda
usarmap
ofilter
, la lista de comp o genexpr es siempre la mejor opción;map
yfilter
puede ser un poco más rápido cuando la función transform / predicate es un Python incorporado implementado en C y el iterable no es trivialmente pequeño, pero siempre son más lentos cuando se necesita unlambda
que la listacomp / genexpr podría evitar.Necesitaba hacer esto con una lista enorme, y duplicar la lista parecía costoso, especialmente porque en mi caso el número de eliminaciones sería muy reducido en comparación con los elementos restantes. Tomé este enfoque de bajo nivel.
Lo que no sé es qué tan eficientes son un par de eliminaciones en comparación con copiar una lista grande. Por favor comente si tiene alguna idea.
fuente
list
una estructura de datos en primer lugar debe considerarse cuidadosamente, ya que la eliminación de la mitad de una lista lleva un tiempo lineal en la longitud de la lista. Si realmente no necesita acceso aleatorio al elemento secuencial k-ésimo, ¿tal vez considereOrderedDict
?newlist = []
, y luegonewlist.append(array[i])
justo antesdel array[i]
?list()
es una lista vinculada, el acceso aleatorio es costoso, silist()
es una matriz, las eliminaciones son costosas porque requieren mover todos los elementos siguientes hacia adelante. Un iterador decente podría mejorar las cosas para la implementación de la lista vinculada. Sin embargo, esto podría ser eficiente en espacio.Puede ser inteligente también crear una nueva lista si el elemento de la lista actual cumple con los criterios deseados.
entonces:
y para evitar tener que volver a codificar todo el proyecto con el nuevo nombre de la lista:
nota, de la documentación de Python:
fuente
Esta respuesta se escribió originalmente en respuesta a una pregunta que desde entonces se ha marcado como duplicada: Eliminar 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á elementos en su lista.
Repasemos lo que sucede cuando ejecutamos su código:
El primer problema es que está pasando 'a' y 'b' para eliminar (), pero remove () solo acepta un único argumento. Entonces, ¿cómo podemos hacer que remove () funcione correctamente con su lista? Necesitamos descubrir cuál es cada elemento de su 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):
¡Ajá! Cada elemento de L1 es en realidad una tupla. Entonces eso es lo que debemos pasar para eliminar (). Las tuplas en python son muy fáciles, simplemente se hacen encerrando valores entre paréntesis. "a, b" no es una tupla, pero "(a, b)" es una tupla. Entonces modificamos su código y lo ejecutamos nuevamente:
Este código se ejecuta sin ningún error, pero veamos la lista que muestra:
¿Por qué (1, -2) todavía está en su lista? Resulta que modificar la lista mientras se usa un bucle para iterar 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 iteraciones del bucle for. Veamos qué sucede si le damos al código anterior una lista más larga:
Como puede deducir de ese resultado, cada vez que la declaración condicional se evalúa como verdadera y se elimina un elemento de la lista, la siguiente iteración del bucle omitirá la evaluación del siguiente elemento de la lista porque sus valores ahora se encuentran 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í:
Sin embargo, la salida será idéntica a la anterior:
Esto se debe a que cuando creamos L2, python en realidad no creó un nuevo objeto. En cambio, simplemente hizo referencia a L2 al mismo objeto que L1. Podemos verificar esto con 'is', que es diferente de simplemente "igual" (==).
Podemos hacer una copia verdadera usando copy.copy (). Entonces todo funciona como se esperaba:
Finalmente, hay una solución más limpia que tener que hacer una copia completamente nueva de L1. La función invertida ():
Desafortunadamente, no puedo describir adecuadamente cómo funciona reverse (). Devuelve un objeto 'listreverseiterator' cuando se le pasa una lista. Para fines prácticos, puede pensar que se trata de una copia inversa de su argumento. Esta es la solución que recomiendo.
fuente
Si desea hacer algo más durante la iteración, puede ser bueno obtener tanto el índice (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.
enumerate
le da acceso al artículo y al índice a la vez.reversed
es para que los índices que vas a eliminar más tarde no cambien en ti.fuente
Es posible que desee utilizar
filter()
disponible como incorporado.Para más detalles ver aquí
fuente
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 (110,000 artículos) y era más inteligente seguir reduciendo la lista.
En primer lugar, deberá reemplazar el bucle foreach con el bucle while ,
El valor de
i
no 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.fuente
Puede probar el bucle for en reversa, por lo que para some_list hará algo como:
De esta manera, el índice está alineado y no sufre las actualizaciones de la lista (independientemente de si resalta el elemento cur o no).
fuente
reversed(list(enumerate(some_list)))
sería más simple que calcular los índices usted mismo.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:
fuente
bad
sucede si quiero eliminar cosas, hacer algo con él y también hacer algo congood
cosas en un ciclo?alist[:]
) Y como podrías estar haciendo algo elegante, en realidad tiene un caso de uso. Buena revisión es buena. Toma mi voto positivo.Necesitaba hacer algo similar y en mi caso el problema era la memoria: necesitaba fusionar 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 evite duplicarlos todos y explotar la memoria. En mi caso, tener los objetos en un diccionario en lugar de una lista funcionó bien:
`` `
`` `
fuente
TLDR:
Escribí una biblioteca que te permite hacer esto:
Si es posible, es mejor usar otro método que no requiera modificar su iterable mientras itera sobre él, pero para algunos algoritmos podría no ser tan sencillo. Entonces, 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 las listas.
Respuesta completa:
Editar: El último ejemplo de código en esta respuesta ofrece un caso de uso de por qué a veces es posible que desee modificar una lista en lugar de utilizar una comprensión de la lista. La primera parte de las respuestas sirve como tutorial de cómo se puede modificar una matriz en su lugar.
La solución se sigue de esto respuesta (para una pregunta relacionada) de senderle. Lo que explica cómo se actualiza el índice de matriz mientras se itera 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 aquíhttps://github.com/alanbacon/FluidIterator
, es solo un archivo único, por lo que no es necesario instalar git. No hay instalador, por lo que deberá asegurarse de que el archivo esté en la ruta de Python. El código ha sido escrito para python 3 y no ha sido probado en python 2.Esto producirá el siguiente resultado:
Arriba hemos usado el
pop
método en el objeto de la lista de fluidos. Otros métodos iterables comunes también se implementan tales comodel fluidL[i]
,.remove
,.insert
,.append
,.extend
. La lista también se puede modificar utilizando sectores (sort
yreverse
métodos no se implementan).La única condición es que solo debe modificar la lista en su lugar, si en algún momento
fluidL
ol
fue reasignado a un objeto de lista diferente, el código no funcionaría. ElfluidL
objeto for todavía usaría el objeto original, pero quedaría fuera de alcance para que lo modifiquemos.es decir
Si queremos acceder al valor de índice actual de la lista, no podemos usar enumerar, ya que esto solo cuenta cuántas veces se ha ejecutado el bucle for. En su lugar, utilizaremos el objeto iterador directamente.
Esto generará lo siguiente:
La
FluidIterable
clase solo proporciona un contenedor para el objeto de lista original. Se puede acceder al objeto original como una propiedad del objeto fluido de la siguiente manera:Se pueden encontrar más ejemplos / pruebas en la
if __name__ is "__main__":
sección al final defluidIter.py
. Vale la pena verlos porque explican lo que sucede en diversas situaciones. Tales como: Reemplazar grandes secciones de la lista usando un segmento. O usando (y modificando) el mismo iterable en bucles anidados.Como dije para comenzar: esta es una solución complicada que perjudicará la legibilidad de su código y dificultará la depuración. Por lo tanto, otras soluciones, como las comprensiones de listas mencionadas en la respuesta de David Raznick, deben considerarse primero. Dicho esto, he encontrado momentos en los que esta clase me ha sido útil y ha sido más fácil de usar que hacer un seguimiento de los índices de elementos que deben eliminarse.
Editar: como se menciona en los comentarios, esta respuesta realmente no presenta un problema para el cual este enfoque proporciona una solución. Trataré de abordar eso aquí:
Las comprensiones de listas proporcionan una forma de generar una nueva lista, pero estos enfoques tienden a ver cada elemento de forma aislada en lugar del estado actual de la lista en su conjunto.
es decir
Pero, ¿qué pasa si el resultado
testFunc
depende de los elementos que ya se han agregadonewList
? ¿O los elementos que todavía están enoldList
eso podrían agregarse a continuación? Todavía puede haber una manera de utilizar una comprensión de la lista, 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 múltiplo de ningún otro elemento.
La salida y la lista reducida final se muestran a continuación
fuente
some_list[:] = [x for x in some_list if not some_condition(x)]
no logra? Sin una respuesta a eso, ¿por qué debería alguien creer que descargar y usar su biblioteca de 600 líneas completa con errores tipográficos y código comentado es una mejor solución a su problema que el de una sola línea? -1.some_list[:] = [x for x in some_list if not some_condition(y)]
dóndey
es un elemento de lista diferentex
. Tampoco sería posible escribirsome_list[:] = [x for x in some_list if not some_condition(intermediateStateOf_some_list)]
.El método más eficaz es la lista por comprensión, muchas personas muestran su caso, por supuesto, también es una buena manera de obtener una
iterator
a travésfilter
.Hay un ejemplo (obtenga las probabilidades en la tupla):
Precaución: tampoco puede manejar iteradores. Los iteradores son a veces mejores que las secuencias.
fuente
El bucle for se repetirá a través del índice.
considera que tienes una lista,
tienes usando la variable de lista llamada
lis
. y usas lo mismo para eliminar ..tu variable
durante la 5ta iteración,
su número 35 no era primo, por lo que lo eliminó de una lista.
y luego el siguiente valor (65) pasa al índice anterior.
así que el puntero de la cuarta iteración se movió al quinto.
Es por eso que su ciclo no cubre 65 ya que se movió al índice anterior.
por lo que no debe hacer referencia a la lista en otra variable que todavía haga referencia al original en lugar de la copia.
entonces copia de la lista usando
list[::]
ahora te dará,
El problema es que eliminó un valor de una lista durante la iteración, luego el índice de su lista colapsará.
para que puedas probar la comprensión en su lugar.
que admite todos los iterables como, list, tuple, dict, string, etc.
fuente
Si desea eliminar elementos de una lista mientras itera, use un ciclo while para que pueda alterar el índice actual y el índice final después de cada eliminación.
Ejemplo:
fuente
Las otras respuestas son correctas, por lo general es una mala idea eliminar de una lista que está iterando. La iteración inversa evita las trampas, pero es mucho más difícil seguir el código que hace eso, por lo que generalmente es mejor usar una comprensión de lista 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 con a
return
o abreak
. Por ejemplo:Esto a menudo es más fácil de entender que una comprensión de la lista cuando realiza algunas operaciones con efectos secundarios en el primer elemento de una lista que cumple alguna condición y luego elimina ese elemento de la lista inmediatamente después.
fuente
Se me ocurren tres enfoques para resolver su problema. Como ejemplo, crearé una lista aleatoria de tuplas
somelist = [(1,2,3), (4,5,6), (3,6,6), (7,8,9), (15,0,0), (10,11,12)]
. La condición que elijo essum of elements of a tuple = 15
. En la lista final solo tendremos esas tuplas cuya suma no es igual a 15.Lo que he elegido es un ejemplo elegido al azar. Siéntase libre de cambiar la lista de tuplas y la condición que he elegido.
Método 1.> Use el marco que había sugerido (donde se completa un código dentro de un bucle for). Utilizo un código pequeño con
del
para eliminar una tupla que cumple con dicha condición. Sin embargo, este método perderá una tupla (que satisface dicha condición) si dos tuplas colocadas consecutivamente cumplen con la condición dada.Método 2.> Construya una nueva lista que contenga elementos (tuplas) donde no se cumpla la condición dada (esto es lo mismo que eliminar elementos de la lista donde se cumple la condición dada). El siguiente es el código para eso:
Método 3.> Encuentre índices donde se cumpla la condición dada y luego use elementos de eliminación (tuplas) correspondientes a esos índices. El siguiente es el código para eso.
El método 1 y el método 2 son más rápidos que el método 3 . Método2 y método3 son más eficientes que método1. Yo prefiero metodo2 . Para el ejemplo mencionado anteriormente,
time(method1) : time(method2) : time(method3) = 1 : 1 : 1.7
fuente
Para cualquier cosa que tenga el potencial de ser realmente grande, utilizo lo siguiente.
Eso debería ser significativamente más rápido que cualquier otra cosa.
fuente
En algunas situaciones, donde está haciendo más que simplemente filtrar una lista de un elemento a la vez, desea que su iteración cambie mientras itera.
Aquí hay un ejemplo donde copiar la lista de antemano es incorrecto, la iteración inversa es imposible y la comprensión de la lista tampoco es una opción.
fuente
Si usará la nueva lista más adelante, simplemente puede establecer el elemento en Ninguno y luego juzgarlo en el ciclo posterior, de esta manera
De esta manera, no necesita copiar la lista y es más fácil de entender.
fuente
suponga una lista de números y desea eliminar todos los no que sean divisibles por 3,
usando
list comprehension
, esto creará una nueva lista y creará un nuevo espacio de memoriausando la
lambda filter
función, esto creará una nueva lista resultante y consumirá espacio de memoriasin consumir espacio de memoria para la nueva lista y modificar la lista existente
fuente