¿Es legítimo eliminar elementos de un diccionario en Python mientras itera sobre él?
Por ejemplo:
for k, v in mydict.iteritems():
if k == val:
del mydict[k]
La idea es eliminar elementos del diccionario que no cumplan una determinada condición, en lugar de crear un nuevo diccionario que sea un subconjunto del que se repite.
¿Es esta una buena solución? ¿Hay formas más elegantes / eficientes?
scripting
dictionary
python
Trilarion
fuente
fuente
Respuestas:
EDITAR:
Esta respuesta no funcionará para Python3 y dará un
RuntimeError
.Esto sucede porque
mydict.keys()
devuelve un iterador, no una lista. Como se señaló en los comentarios, simplemente conviértalomydict.keys()
en una listalist(mydict.keys())
y debería funcionar.Una prueba simple en la consola muestra que no puede modificar un diccionario mientras itera sobre él:
Como se indica en la respuesta de delnan, la eliminación de entradas causa problemas cuando el iterador intenta pasar a la siguiente entrada. En su lugar, utilice el
keys()
método para obtener una lista de las claves y trabaje con eso:Si necesita eliminar según el valor de los elementos, utilice el
items()
método en su lugar:fuente
for k, v in list(mydict.items()):
que funciona bien en Python 3. Lo mismo parakeys()
convertirselist(keys())
.RuntimeError: dictionary changed size during iteration
for k in list(mydict.keys()):
ya que python3 hace que el método keys () sea un iterador, y también no permite eliminar elementos dict durante la iteración. Al agregar una llamada list (), convierte el iterador keys () en una lista. Entonces, cuando estás en el cuerpo del bucle for, ya no estás iterando sobre el diccionario.También puedes hacerlo en dos pasos:
Mi enfoque favorito suele ser simplemente hacer un nuevo dict:
fuente
remove
enfoque de bucle.for k in [k for k in mydict if k == val]: del mydict[k]
No puede modificar una colección mientras la itera. Esa forma es una locura: sobre todo, si se le permitiera eliminar y eliminar el elemento actual, el iterador tendría que avanzar (+1) y la siguiente llamada a
next
lo llevaría más allá de eso (+2), por lo que termina omitiendo un elemento (el que está justo detrás del que eliminó). Tienes dos opciones:.keys()
et al para esto (en Python 3, pase el iterador resultante alist
). Sin embargo, podría ser un gran desperdicio de espacio.mydict
como de costumbre, guardando las claves para eliminar en una colección separadato_delete
. Cuando termine de iterarmydict
, elimine todos los elementosto_delete
demydict
. Ahorra algo de espacio (dependiendo de cuántas claves se eliminen y cuántas permanecen) durante el primer enfoque, pero también requiere algunas líneas más.fuente
You can't modify a collection while iterating it.
esto es correcto para dictados y amigos, pero puede modificar las listas durante la iteración:L = [1,2,None,4,5] <\n> for n,x in enumerate(L): <\n\t> if x is None: del L[n]
can't
es correcto solo para dict y amigos, mientras que debería sershouldn't
para listas.En su lugar, repite una copia, como la que devuelve
items()
:fuente
del v
directamente, por lo que has hecho una copia de cada v que nunca vas a usar y tienes que acceder a los elementos por clave de todos modos.dict.keys()
Es una mejor opción.v
como criterio para la eliminación.dict.items()
devuelve un iterador en lugar de una copia. Vea el comentario para la respuesta de Blair , que (tristemente) también asume la semántica de Python 2.Es más limpio de usar
list(mydict)
:Esto corresponde a una estructura paralela para listas:
Ambos funcionan en python2 y python3.
fuente
Puedes usar un diccionario de comprensión.
d = {k:d[k] for k in d if d[k] != val}
fuente
d
en su lugar.Con python3, iterar en dic.keys () elevará el error de tamaño del diccionario. Puede usar esta forma alternativa:
Probado con python3, funciona bien y el error "el diccionario cambió de tamaño durante la iteración " no aparece:
fuente
Primero puede crear una lista de claves para eliminar y luego iterar sobre esa lista eliminándolas.
fuente
Hay una manera que puede ser adecuada si los elementos que desea eliminar siempre están al "comienzo" de la iteración dict
El "comienzo" solo se garantiza que sea consistente para ciertas versiones / implementaciones de Python. Por ejemplo de What's New In Python 3.7
De esta forma, se evita una copia del dict que sugieren muchas de las otras respuestas, al menos en Python 3.
fuente
Probé las soluciones anteriores en Python3, pero esta parece ser la única que funciona para mí al almacenar objetos en un dict. Básicamente, hace una copia de su dict () e itera sobre eso mientras elimina las entradas en su diccionario original.
fuente