Eliminar un elemento de un diccionario

1789

¿Hay alguna forma de eliminar un elemento de un diccionario en Python?

Además, ¿cómo puedo eliminar un elemento de un diccionario para devolver una copia (es decir, sin modificar el original)?

5
  • 14
    ¿Por qué necesita una función que devuelva un diccionario, cuando puede modificar el diccionario directamente? 1 de mayo de 2011 a las 1:26
  • 7
    El diccionario popmétodo cambia el diccionario en el lugar . Por lo tanto, altera la referencia al diccionario que se pasó del llamador a la "función auxiliar". Así que la "función auxiliar" no necesita devolver nada, ya que la referencia original al diccionario en el llamador ya estará alterada. No asigne el retorno de dict.pop()a nada si no lo necesita. EG: do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_key. Úselo deepcopy(my_dict)si es necesario. 1 de julio de 2016 a las 23:51
  • 1
    Dado que el título original no estaba de acuerdo con los detalles y excluía específicamente la solución obvia d.pop(), arreglé el título para hacer la pregunta especificada en los detalles.
    smci
    14 de mayo de 2018 a las 22:59
  • 1
    Deberíamos agregar una advertencia preguntando si realmente desea hacer esto, ya que si lo hace N veces en un diccionario con elementos E, perderá (/ usará) memoria O (N * E) con todas las copias profundas. Si simplemente desea una copia de solo lectura (copia superficial), hágalo d.pop(key). Pero si algo modifica la copia superficial, tiene un problema bien conocido con el alias . Ayuda si nos cuenta el contexto más amplio. (¿Hay algo más que modifique los valores de dict? ¿Está tratando de iterar destructivamente sobre una lista? Si no es así, ¿qué?)
    smci
    15 de mayo de 2018 a las 1:13
  • 5
    "¿Por qué necesita una función que devuelva un diccionario, cuando puede modificar el diccionario directamente?" ¿Quizás porque desea escribir funciones puras que no modifiquen sus parámetros? 7 de enero de 2019 a las 4:39
2117

La deldeclaración elimina un elemento:

del d[key]

Tenga en cuenta que esto muta el diccionario existente, por lo que el contenido del diccionario cambia para cualquier otra persona que tenga una referencia a la misma instancia. Para devolver un diccionario nuevo , haga una copia del diccionario:

def removekey(d, key):
    r = dict(d)
    del r[key]
    return r

El dict()constructor hace una copia superficial . Para hacer una copia en profundidad, consulte el copymódulo .


Tenga en cuenta que hacer una copia para cada dictado del/ tarea / etc. significa que está pasando de tiempo constante a tiempo lineal, y también utilizando espacio lineal. Para dictados pequeños, esto no es un problema. Pero si planea hacer muchas copias de dictados grandes, probablemente desee una estructura de datos diferente, como un HAMT (como se describe en esta respuesta ).

12
  • 18
    Ese es un gran punto sobre la mutabilidad de los diccionarios +1, aunque no puedo pensar en un momento en el que quisiera copias del diccionario, siempre he confiado en que la copia de "todos" sea la misma. gran punto.
    tMC
    30/04/11 a las 21:38
  • 36
    @tMC Si edita el dictmientras lo recorre, le dará un error:RuntimeError: dictionary changed size during iteration 29 de agosto de 2013 a las 8:59
  • 19
    ¿Qué pasa con el popmétodo que de hecho hace lo mismo? ¿No es más pitónico? (siendo el método de dict, no una palabra reservada especial)?
    Serge
    17 feb 2014 a las 9:48
  • 25
    Esta respuesta tiene una debilidad, podría ser engañosa. Los lectores pueden malinterpretar que dict (d) les puede dar una copia con 'd'. Pero es una copia incompleta. Cuando solo realiza operaciones de teclas del, está bien. Pero cuando quiere hacer algo más en un dictado anidado, modificar 'r' usando ese método de copia puede causar un cambio en la 'd' original. Para obtener una copia auténtica, primero necesita 'importar copia' y luego 'r = copy.deepcopy (d)'.
    Zen
    23/07/14 a las 10:34
  • 13
    @Zen: Muy bien, he agregado una nota sobre copia superficial versus copia profunda. 24 de julio de 2014 a las 3:33
383

pop muta el diccionario.

 >>> lol = {"hello": "gdbye"}
 >>> lol.pop("hello")
     'gdbye'
 >>> lol
     {}

Si desea conservar el original, simplemente puede copiarlo.

4
  • 62
    "del" está bien, pero "pop" parece más "Pythonic", en mi opinión. 6 de mayo de 2017 a las 2:27
  • 2
    @ivanleoncz ¿por qué?
    kevr
    23 de enero de 2018 a las 0:45
  • 5
    popdevuelve el valor que fue 'reventado', lo que le permite usar este valor por cualquier motivo adicional. Si no es más "Pythonic", diría que parece mejor, seguro :). No es un dict, pero funciona de la misma manera para ambos: github.com/ivanlmj/python-prototypes/blob/master/3.4/… 23 de enero de 2018 a las 0:59
  • 15
    @ivanleoncz También es mejor por una razón más, popse le puede proporcionar un valor predeterminado que se devolverá cuando falte una clave en dict. Es bueno cuando necesita quitar algunas claves pero algunas de ellas pueden faltar; delarrojaría KeyErroren tal caso.
    itachi
    11/03/20 a las 1:56
101

Creo que tu solución es la mejor forma de hacerlo. Pero si desea otra solución, puede crear un nuevo diccionario usando las claves del diccionario antiguo sin incluir su clave especificada, como esta:

>>> a
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> {i:a[i] for i in a if i!=0}
{1: 'one', 2: 'two', 3: 'three'}
5
  • 3
    Realmente genial. Me gusta el método rápido para filtrar un diccionario sin definir una nueva función.
    Joe J
    16/07/2013 a las 22:15
  • 8
    Para aquellos que no estén familiarizados con las comprensiones, también puede hacer algo como esto: {i:a[i] for i in a if i not in [0, 1, 2]}si desea eliminar varios elementos. 4 feb 2015 a las 20:01
  • 22
    Mejor sería {k:v for k,v in a.items() if k != 0}, creo.
    rlbond
    13/02/2017 a las 23:01
  • 1
    La mejor solución para eliminar un artículo por clave y devolver el resultado del nuevo dictado en la misma línea. Por ejemplo, si necesita usar un dictado ya construido sin un solo elemento como **kwargs,some_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
    Cole
    19/11/2017 a las 21:14
  • 1
    La mejor solución aquí. Un trazador de líneas y no cambia el diccionario original. 11 de mayo de 2018 a las 13:52
75

Hay muchas buenas respuestas, pero quiero enfatizar una cosa.

Puede utilizar tanto el dict.pop()método como una deldeclaración más genérica para eliminar elementos de un diccionario. Ambos modifican el diccionario original, por lo que debe hacer una copia (consulte los detalles a continuación).

Y ambos generarán un KeyErrorsi la clave que les proporcionas no está presente en el diccionario:

key_to_remove = "c"
d = {"a": 1, "b": 2}
del d[key_to_remove]  # Raises `KeyError: 'c'`

y

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove)  # Raises `KeyError: 'c'`

Tienes que encargarte de esto:

capturando la excepción:

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    del d[key_to_remove]
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

y

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    d.pop(key_to_remove)
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

realizando una verificación:

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    del d[key_to_remove]

y

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    d.pop(key_to_remove)

pero pop()también hay una forma mucho más concisa: proporcione el valor de retorno predeterminado:

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove, None)  # No `KeyError` here

A menos que utilice pop()para obtener el valor de la eliminación de una clave, puede proporcionar cualquier cosa que no sea necesaria None. Aunque podría ser que usar delcon incheque sea un poco más rápido debido a que pop()es una función con sus propias complicaciones que causan gastos generales. Por lo general, no es el caso, por lo que pop()el valor predeterminado es lo suficientemente bueno.


En cuanto a la pregunta principal, tendrás que hacer una copia de tu diccionario, para guardar el diccionario original y tener uno nuevo sin que se elimine la clave.

Algunas otras personas aquí sugieren hacer una copia completa (profunda) con copy.deepcopy(), que podría ser una exageración, una copia "normal" (superficial), usando copy.copy()o dict.copy(), podría ser suficiente. El diccionario mantiene una referencia al objeto como valor para una clave. Entonces, cuando elimina una clave de un diccionario, esta referencia se elimina, no el objeto al que se hace referencia. El recolector de basura puede eliminar el objeto en sí más tarde automáticamente, si no hay otras referencias para él en la memoria. Hacer una copia profunda requiere más cálculos en comparación con la copia superficial, por lo que disminuye el rendimiento del código al hacer la copia, desperdiciando memoria y proporcionando más trabajo al GC; a veces, la copia superficial es suficiente.

Sin embargo, si tiene objetos mutables como valores de diccionario y planea modificarlos más adelante en el diccionario devuelto sin la clave, debe hacer una copia en profundidad.

Con copia superficial:

def get_dict_wo_key(dictionary, key):
    """Returns a **shallow** copy of the dictionary without a key."""
    _dict = dictionary.copy()
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

Con copia profunda:

from copy import deepcopy


def get_dict_wo_key(dictionary, key):
    """Returns a **deep** copy of the dictionary without a key."""
    _dict = deepcopy(dictionary)
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}
65

La declaración del es lo que estás buscando. Si tiene un diccionario llamado foo con una tecla llamada 'bar', puede eliminar 'bar' de foo así:

del foo['bar']

Tenga en cuenta que esto modifica permanentemente el diccionario en el que se está operando. Si desea conservar el diccionario original, deberá crear una copia de antemano:

>>> foo = {'bar': 'baz'}
>>> fu = dict(foo)
>>> del foo['bar']
>>> print foo
{}
>>> print fu
{'bar': 'baz'}

La dictllamada hace una copia superficial. Si desea una copia profunda, use copy.deepcopy.

Aquí hay un método que puede copiar y pegar, para su conveniencia:

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy
8
  • 1
    @ pythonian29033, en realidad, no . La respuesta aceptada funciona como se esperaba: devuelve el dict sin una tecla. El enfoque de esta respuesta muta el dict original;) ​​Hay una diferencia significativa 28 de mayo de 2017 a las 20:16
  • 1
    @ arussell84, ¿por qué >>>se usa a menudo en los ejemplos de python? Sí, python-doc contiene muchas de esas cosas. Pero tal código no es conveniente para copiar y pegar . Estoy confundido... 28 de mayo de 2017 a las 20:19
  • @maxkoryukov ¡sí que lo hace! pero esa función y esta respuesta es exactamente la misma, con la excepción de que esa respuesta está dentro de una función. y no debe haber estado codificando en python por un tiempo, >>>imita la notación de escucha de python en modo cli 29 de mayo de 2017 a las 9:22
  • 2
    @ pythonian29033 acerca de >>>. Sí, es estilo REPL, pero hablemos con franqueza: el único hombre había escrito esta muestra y 1000 la han leído. Creo que sería genial escribir ejemplos de manera que permitan copiar y ejecutar fácilmente. No me gusta quitar estos soportes angulares a mano. O copiar línea por línea ... Entonces no entiendo: ¿por qué estos ángulos todavía están ahí))) ¿Puede ser que no sepa algo? 29 de mayo de 2017 a las 9:56
  • 3
    Agregué una función que se puede copiar / pegar, para su conveniencia. 30 de mayo de 2017 a las 14:28
36
+50

… how can I delete an item from a dictionary to return a copy (i.e., not modifying the original)?

A dictes la estructura de datos incorrecta para usar para esto.

Claro, copiar el dict y sacar de la copia funciona, y también lo hace la construcción de un dict nuevo con comprensión, pero toda esa copia lleva tiempo: ha reemplazado una operación de tiempo constante por una de tiempo lineal. Y todas esas copias vivas a la vez ocupan espacio: espacio lineal por copia.

Otras estructuras de datos, como los intentos mapeados de matriz hash , están diseñadas exactamente para este tipo de caso de uso: agregar o eliminar un elemento devuelve una copia en tiempo logarítmico, compartiendo la mayor parte de su almacenamiento con el original . 1

Por supuesto que hay algunas desventajas. El rendimiento es logarítmico en lugar de constante (aunque con una base grande, generalmente 32-128). Y, aunque puede hacer que la API no mutante sea idéntica a dict, la API "mutante" es obviamente diferente. Y, sobre todo, no se incluyen baterías HAMT con Python. 2

La pyrsistentbiblioteca es una implementación bastante sólida de reemplazos de dictados basados ​​en HAMT (y varios otros tipos) para Python. Incluso tiene una ingeniosa API de evolver para trasladar el código mutante existente al código persistente de la manera más fluida posible. Pero si desea ser explícito sobre la devolución de copias en lugar de mutar, simplemente utilícelo así:

>>> from pyrsistent import m
>>> d1 = m(a=1, b=2)
>>> d2 = d1.set('c', 3)
>>> d3 = d1.remove('a')
>>> d1
pmap({'a': 1, 'b': 2})
>>> d2
pmap({'c': 3, 'a': 1, 'b': 2})
>>> d3
pmap({'b': 2})

Eso d3 = d1.remove('a')es exactamente lo que pide la pregunta.

Si tiene estructuras de datos mutables como dicte listincrustadas en el pmap, todavía tendrá problemas de alias; solo puede solucionarlo si se vuelve inmutable hasta el final, incrustando pmaps y pvectors.


1. Los HAMT también se han vuelto populares en lenguajes como Scala, Clojure, Haskell porque funcionan muy bien con la programación sin bloqueo y la memoria transaccional de software, pero ninguno de ellos es muy relevante en Python.

2. De hecho, no es un HAMT en el stdlib, que se utiliza en la ejecución de contextvars. La PEP retirada anteriormente explica por qué. Pero este es un detalle de implementación oculto de la biblioteca, no un tipo de colección pública.

21
d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d

Resultado: d = {1: 2, '2': 3}

14

Simplemente llame a del d ['clave'].

Sin embargo, en producción, siempre es una buena práctica comprobar si existe 'clave' en d.

if 'key' in d:
    del d['key']
2
  • 8
    Mmmm, no, en producción es mejor seguir la ideología EAFP . Simplemente quite la llave en el try-exceptbloque. Al menos, esta será una operación atómica;) 28 de mayo de 2017 a las 20:09
  • 1
    Y si quieres ser conciso d.pop('key', None), úsalo, es un delineador. Pero la cuestión real era conseguir el diccionario sin una clave y no modificar el dict. Entonces comprensiones - es una buena opción aquí;) 28 de mayo de 2017 a las 20:14
8
# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)
0
7

No, no hay otra forma que

def dictMinus(dct, val):
   copy = dct.copy()
   del copy[val]
   return copy

Sin embargo, a menudo crear copias de diccionarios ligeramente alterados probablemente no sea una buena idea porque resultará en demandas de memoria comparativamente grandes. Por lo general, es mejor registrar el diccionario antiguo (si es necesario) y luego modificarlo.

0
5

Aquí un enfoque de diseño de nivel superior:

def eraseElement(d,k):
    if isinstance(d, dict):
        if k in d:
            d.pop(k)
            print(d)
        else:
            print("Cannot find matching key")
    else:
        print("Not able to delete")


exp = {'A':34, 'B':55, 'C':87}
eraseElement(exp, 'C')

Estoy pasando el diccionario y la clave que quiero a mi función, valida si es un diccionario y si la clave está bien, y si ambos existen, elimina el valor del diccionario e imprime las sobras.

Producción: {'B': 55, 'A': 34}

¡Espero que ayude!

0
3
>>> def delete_key(dict, key):
...     del dict[key]
...     return dict
... 
>>> test_dict = {'one': 1, 'two' : 2}
>>> print delete_key(test_dict, 'two')
{'one': 1}
>>>

esto no hace ningún manejo de errores, asume que la clave está en el dict, es posible que desee verificar eso primero y raisesi no es

1
  • 12
    ¿En qué se diferencia tu método de simplemente del test_dict[key]? 14/12/15 a las 20:40
3

El siguiente fragmento de código lo ayudará definitivamente, he agregado comentarios en cada línea que lo ayudarán a comprender el código.

def execute():
   dic = {'a':1,'b':2}
   dic2 = remove_key_from_dict(dic, 'b')  
   print(dict2)           # {'a': 1}
   print(dict)            # {'a':1,'b':2}

def remove_key_from_dict(dictionary_to_use, key_to_delete):
   copy_of_dict = dict(dictionary_to_use)     # creating clone/copy of the dictionary
   if key_to_delete in copy_of_dict :         # checking given key is present in the dictionary
       del copy_of_dict [key_to_delete]       # deleting the key from the dictionary 
   return copy_of_dict                        # returning the final dictionary

o también puede usar dict.pop ()

d = {"a": 1, "b": 2}

res = d.pop("c")  # No `KeyError` here
print (res)       # this line will not execute

o el mejor enfoque es

res = d.pop("c", "key not found")
print (res)   # key not found
print (d)     # {"a": 1, "b": 2}

res = d.pop("b", "key not found")
print (res)   # 2
print (d)     # {"a": 1}
2

Aquí hay otra variación que usa la comprensión de listas:

original_d = {'a': None, 'b': 'Some'}
d = dict((k,v) for k, v in original_d.iteritems() if v)
# result should be {'b': 'Some'}

El enfoque se basa en una respuesta de esta publicación: forma eficiente de eliminar claves con cadenas vacías de un dictado

4
  • 1
    Si va a responder una pregunta de hace años que ya tiene una respuesta simple, apropiada y aceptada, al menos asegúrese de que su respuesta sea correcta. Esto no hace lo que pidió el OP. 30 de agosto de 2013 a las 1:12
  • Por lo general, no verifico las fechas de las preguntas que creo que podrían tener información valiosa agregada. Además, según uno de los comentarios sobre la pregunta a la que me vinculé: "Por lo general, esto es exactamente lo que alguien quiere y es probablemente lo que necesita el OP, pero no es lo que el OP pidió" stackoverflow.com/questions/12118695/… I sabía que no era una respuesta directa a la pregunta; más bien una expansión de las opciones. 30 de agosto de 2013 a las 1:49
  • 3
    Esta respuesta, aunque no completa, nos permite aprender que también podemos eliminar elementos con condiciones if. simplemente cambiando if va las if k is not 'a'respuestas de la op. Pero no creo que sea una forma eficiente, esto elimina el elemento en O (n) en lugar de O (log n) como lo hace pop o del.
    holgac
    10 de mayo de 2014 a las 14:24
  • @holgac al copiar el diccionario se ejecuta en O (n), por lo que su argumento sobre la eficiencia no es correcto dado que OP requería que el dictado original permaneciera sin modificar, lo que requiere que se copie el dictado.
    Phil
    8 de septiembre de 2020 a las 12:25
0
    species = {'HI': {'1': (1215.671, 0.41600000000000004),
  '10': (919.351, 0.0012),
  '1025': (1025.722, 0.0791),
  '11': (918.129, 0.0009199999999999999),
  '12': (917.181, 0.000723),
  '1215': (1215.671, 0.41600000000000004),
  '13': (916.429, 0.0005769999999999999),
  '14': (915.824, 0.000468),
  '15': (915.329, 0.00038500000000000003),
 'CII': {'1036': (1036.3367, 0.11900000000000001), '1334': (1334.532, 0.129)}}

El siguiente código hará una copia de dict speciesy eliminará elementos que no están entrans_HI

trans_HI=['1025','1215']
for transition in species['HI'].copy().keys():
    if transition not in trans_HI:
        species['HI'].pop(transition)
0

puede probar mi método. En una línea.

yourList = [{'key':'key1','version':'1'},{'key':'key2','version':'2'},{'key':'key3','version':'3'}]
resultList = [{'key':dic['key']} for dic in yourList if 'key' in dic]
print(resultList)