¿Cómo elimino elementos de un diccionario mientras itero sobre él?

8 minutos de lectura

¿Puedo eliminar elementos de un diccionario en Python mientras itero sobre él?

Quiero eliminar del diccionario los elementos que no cumplen una determinada condición, en lugar de crear un diccionario completamente nuevo. ¿Es la siguiente una buena solución, o hay mejores formas?

for k, v in mydict.items():
    if k == val:
        del mydict[k]

  • Una pregunta relacionada con respuestas muy interesantes: stackoverflow.com/questions/9023078/….

    – máx.

    17 de agosto de 2012 a las 8:19

  • Observación tonta: todo tu ciclo no tiene sentido si solo estás buscando una clave específica. Podrías reemplazarlo con try: del mydict[val] except KeyError: pass o como una sola línea, con mydict.pop(val, None)ambos serían O(1) operaciones, no O(n). Sin embargo, la pregunta sigue siendo válida si la condición para la eliminación es más que “igual a algún valor”.

    – ShadowRanger

    02/03/2017 a las 21:00

  • Muchos de los comentarios y respuestas aquí suponen que esto es imposible por definición, pero no lo es, es solo que Python no proporciona una implementación. Por ejemplo, en Java, es posible eliminar el valor iterado más recientemente de un HashMap usando el iterador remove() método. Esto es muy deseable porque, como muestran todos los ejemplos aquí, solucionarlo es molesto, propenso a errores y una pérdida de memoria y tiempo.

    –Ken Williams

    8 de mayo de 2020 a las 16:44

avatar de usuario
Blair

Para Pitón 3+:

>>> mydict
{'four': 4, 'three': 3, 'one': 1}

>>> for k in list(mydict.keys()):
...     if mydict[k] == 3:
...         del mydict[k]

>>> mydict
{'four': 4, 'one': 1}

Las otras respuestas funcionan bien con Pitón 2 pero levanta un RuntimeError por Pitón 3:

RuntimeError: el diccionario cambió de tamaño durante la iteración.

Esto sucede porque mydict.keys() devuelve un iterador, no una lista. Como se señaló en los comentarios, simplemente convierta mydict.keys() a una lista por list(mydict.keys()) y debería funcionar.


Para Pitón 2:

Una simple prueba en la consola muestra que no puede modificar un diccionario mientras lo itera:

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}

>>> for k, v in mydict.iteritems():
...    if k == 'two':
...        del mydict[k]

------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

Como se indica en la respuesta de delnan, eliminar 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 trabajar con eso:

>>> for k in mydict.keys():
...    if k == 'two':
...        del mydict[k]

>>> mydict
{'four': 4, 'three': 3, 'one': 1}

Si necesita eliminar en función del valor de los elementos, utilice el items() método en su lugar:

>>> for k, v in mydict.items():
...     if v == 3:
...         del mydict[k]

>>> mydict
{'four': 4, 'one': 1}

  • Para profundizar en la elaboración de @max, funcionará si convierte el código anterior con 2to3. Uno de los reparadores predeterminados hará que el bucle se vea como for k, v in list(mydict.items()): que funciona bien en Python 3. Lo mismo para keys() convirtiéndose list(keys()).

    – Walter Mundt

    15 de agosto de 2012 a las 17:59

  • @TomášZato como señaló Walter, para python3 necesitas usar for k in list(mydict.keys()): como python3 hace que el método keys() sea un iterador, y también no permite eliminar elementos dictados durante la iteración. Al agregar una llamada list(), convierte el iterador keys() en una lista. Entonces, cuando está en el cuerpo del bucle for, ya no está iterando sobre el diccionario en sí.

    –Geoff Crompton

    22 de marzo de 2017 a las 23:12

  • Voluntad list hacer una copia de las llaves, o solo apuntar a cada una de ellas?

    – matanster

    12 de septiembre de 2018 a las 6:11

  • @matanster list básicamente convierte un iterator en una lista de keys.

    – CodeIt

    5 de octubre de 2019 a las 14:28

  • optaría por for key, value in my_dict.copy().items(): entonces está iterando a través de un diccionario duplicado y eliminando del original. Mantiene la limpieza de la tipificación de datos.

    – xle

    27 de enero de 2021 a las 15:18


avatar de usuario
jochen ritzel

También puedes hacerlo en dos pasos:

remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]

Mi enfoque favorito suele ser simplemente hacer un nuevo dict:

# Python 2.7 and 3.x
mydict = { k:v for k,v in mydict.items() if k!=val }
# before Python 2.7
mydict = dict((k,v) for k,v in mydict.iteritems() if k!=val)

  • @senderle: Desde 2.7 en realidad.

    – Jochen Ritzel

    22 de marzo de 2011 a las 0:17

  • El enfoque de comprensión dict hace una copia del diccionario; afortunadamente, los valores al menos no se copian en profundidad, solo se vinculan. Aún así, si tienes muchas llaves, podría ser malo. Por eso me gusta el remove enfoque de bucle más.

    – máx.

    26 de enero de 2012 a las 17:28

  • También puedes combinar los pasos: for k in [k for k in mydict if k == val]: del mydict[k]

    – AXO

    16 de enero de 2017 a las 11:14


  • la primera solución es la única eficiente en grandes dictados en este hilo hasta ahora, ya que no hace una copia completa.

    – kxr

    28/04/2017 a las 21:05

avatar de usuario
Ignacio Vázquez-Abrams

Iterar sobre una copia en su lugar, como la devuelta por items():

for k, v in list(mydict.items()):

  • Eso no tiene mucho sentido, entonces no puedes del v directamente, por lo que ha hecho una copia de cada v que nunca va a usar y de todos modos tiene que acceder a los elementos por clave. dict.keys() es una mejor opción.

    – jscs

    22 de marzo de 2011 a las 2:21

  • @Josh: Todo depende de cuánto necesitará usar v como criterio de eliminación.

    – Ignacio Vázquez-Abrams

    22 de marzo de 2011 a las 7:00

  • Bajo Python 3, dict.items() devuelve un iterador en lugar de una copia. Consulte el comentario de la respuesta de Blair, que (lamentablemente) también asume la semántica de Python 2.

    –Cecil Curry

    22 de febrero de 2016 a las 5:37


No puede modificar una colección mientras la itera. De esa manera se encuentra la locura, en particular, 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á (+2), por lo que terminaría omitiendo un elemento (el que está justo detrás del que eliminó). Tienes dos opciones:

  • Copie todas las claves (o valores, o ambos, según lo que necesite), luego itere sobre ellos. Puedes usar .keys() et al para esto (en Python 3, pase el iterador resultante a list). Sin embargo, podría ser un gran desperdicio en cuanto al espacio.
  • iterar sobre mydict como de costumbre, guardando las claves para eliminar en una colección separada to_delete. Cuando termines de iterar mydictelimine todos los elementos de to_delete de mydict. Ahorra algo de espacio (dependiendo de cuántas claves se eliminen y cuántas permanezcan) en el primer enfoque, pero también requiere algunas líneas más.

avatar de usuario
rsanden

Es más limpio de usar list(mydict):

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k in list(mydict):
...     if k == 'three':
...         del mydict[k]
... 
>>> mydict
{'four': 4, 'two': 2, 'one': 1}

Esto corresponde a una estructura paralela para listas:

>>> mylist = ['one', 'two', 'three', 'four']
>>> for k in list(mylist):                            # or mylist[:]
...     if k == 'three':
...         mylist.remove(k)
... 
>>> mylist
['one', 'two', 'four']

Ambos funcionan en python2 y python3.

  • Esto no es bueno en caso de que su conjunto de datos sea grande. Esto es copiar todos los objetos en la memoria, ¿verdad?

    – AFP_555

    9 de febrero de 2020 a las 3:46

  • @AFP_555 Sí, mi objetivo aquí es un código pitónico limpio, paralelo. Si necesita eficiencia de la memoria, el mejor enfoque que conozco es iterar y crear una lista de claves para eliminar o un nuevo dictado de elementos para guardar. La belleza es mi prioridad con Python; para conjuntos de datos grandes, estoy usando Go o Rust.

    – rsanden

    10 de febrero de 2020 a las 4:27


Con python3, iterar en dic.keys() generará el error de tamaño del diccionario. Puedes usar esta forma alternativa:

Probado con python3, funciona bien y el error “el diccionario cambió de tamaño durante la iteración” no se levanta:

my_dic = { 1:10, 2:20, 3:30 }
# Is important here to cast because ".keys()" method returns a dict_keys object.
key_list = list( my_dic.keys() )

# Iterate on the list:
for k in key_list:
    print(key_list)
    print(my_dic)
    del( my_dic[k] )


print( my_dic )
# {}

  • Esto no es bueno en caso de que su conjunto de datos sea grande. Esto es copiar todos los objetos en la memoria, ¿verdad?

    – AFP_555

    9 de febrero de 2020 a las 3:46

  • @AFP_555 Sí, mi objetivo aquí es un código pitónico limpio, paralelo. Si necesita eficiencia de la memoria, el mejor enfoque que conozco es iterar y crear una lista de claves para eliminar o un nuevo dictado de elementos para guardar. La belleza es mi prioridad con Python; para conjuntos de datos grandes, estoy usando Go o Rust.

    – rsanden

    10 de febrero de 2020 a las 4:27


avatar de usuario
Aarón

Puede utilizar un diccionario de comprensión.

d = {k:d[k] for k in d if d[k] != val}

  • Este es el más pitónico.

    – Yehosef

    13 de enero de 2020 a las 9:56

  • Pero crea un nuevo diccionario en lugar de modificar d en su lugar.

    – Aristide

    15 de abril de 2020 a las 15:46

¿Ha sido útil esta solución?