Supprimer un élément d'un dictionnaire lorsque sa clé est inconnue


112

Quelle est la meilleure façon de supprimer un élément d'un dictionnaire par valeur, c'est-à-dire lorsque la clé de l'élément est inconnue? Voici une approche simple:

for key, item in some_dict.items():
    if item is item_to_remove:
        del some_dict[key]

Y a-t-il de meilleures façons? Y a-t-il un problème avec la mutation (suppression d'éléments) du dictionnaire lors de son itération?


1
La raison soulignée pour interdire la mutation de dict lors de son itération est parce qu'en interne il y a un ordre d'itération, si vous changez les clés, l'ordre serait miné, ce qui entraîne un comportement inconnu.
Spectral

Réponses:


92

Sachez que vous testez actuellement l'identité d'objet ( isne renvoie que Truesi les deux opérandes sont représentés par le même objet en mémoire - ce n'est pas toujours le cas avec deux objets qui se comparent à ==). Si vous le faites exprès, vous pouvez réécrire votre code comme

some_dict = {key: value for key, value in some_dict.items() 
             if value is not value_to_remove}

Mais cela peut ne pas faire ce que vous voulez:

>>> some_dict = {1: "Hello", 2: "Goodbye", 3: "You say yes", 4: "I say no"}
>>> value_to_remove = "You say yes"
>>> some_dict = {key: value for key, value in some_dict.items() if value is not value_to_remove}
>>> some_dict
{1: 'Hello', 2: 'Goodbye', 3: 'You say yes', 4: 'I say no'}
>>> some_dict = {key: value for key, value in some_dict.items() if value != value_to_remove}
>>> some_dict
{1: 'Hello', 2: 'Goodbye', 4: 'I say no'}

Donc, vous voulez probablement !=au lieu de is not.


2
Est-ce une compression de dictionnaire? Quand ont-ils été ajoutés?
Buttons840

4
vous pouvez utiliser some_dict.iteritems()ici et mettre foret ifdéclarations sur des lignes séparées pour plus de lisibilité
jfs

3
Je pense que des compréhensions de dictionnaire ont été ajoutées dans Python 2.7.
mithrandi

2
@JF Sebastian: Je suis sur Python 3 et je le suis iteritemsmaintenant items. En Python 2.7, iteritems()c'est en effet mieux.
Tim Pietzcker

1
@ Buttons840 ils sont appelés compréhensions de dict dans PEP 274 ou affichages de dictionnaire . comme le PEP le dit, ils ont été ajoutés en 2.7 comme exploits 3.x rétroportés. Sinon, vous pouvez alimenter dict()avec une expression de générateur appropriée, qui est 2.4. meta: peut parcourir les peps ici pour trouver des trucs.
n611x007

120

La dict.pop(key[, default])méthode vous permet de supprimer des éléments lorsque vous connaissez la clé. Il renvoie la valeur à la clé s'il supprime l'élément, sinon il renvoie ce qui est passé comme default. Voir les documents . '

Exemple:

>>> dic = {'a':1, 'b':2}
>>> dic
{'a': 1, 'b': 2}
>>> dic.pop('c', 0)
0
>>> dic.pop('a', 0)
1
>>> dic
{'b': 2}

4
OP a demandé quand la clé est inconnue
nmz787


42

Une simple comparaison entre del et pop () :

import timeit
code = """
results = {'A': 1, 'B': 2, 'C': 3}
del results['A']
del results['B']
"""
print timeit.timeit(code, number=100000)
code = """
results = {'A': 1, 'B': 2, 'C': 3}
results.pop('A')
results.pop('B')
"""
print timeit.timeit(code, number=100000)

résultat:

0.0329667857143
0.0451040902256

Ainsi, del est plus rapide que pop () .


6
Cependant, la différence de performances n'est pas grande, et si vous voulez éviter de déclencher une exception, vous pouvez fournir un deuxième argument à pop()(comme @ n-1-1 ci-dessus) - ce qui n'est pas une option pour l' delopérateur.
Alex Dupuy du

1
Accessoire à la question, mais j'avais aussi du mal à comprendre timeit. Merci pour cet exemple clair.
Adam_G

OP a demandé quand la clé est inconnue. Cette réponse suppose que la clé est connue.
Jean-François Corbett

7

items()renvoie une liste, et c'est cette liste que vous itérez, donc la mutation du dict dans la boucle n'a pas d'importance ici. Si vous utilisiez à la iteritems()place, la mutation du dict dans la boucle serait problématique , et de même pourviewitems() Python 2.7.

Je ne peux pas penser à une meilleure façon de supprimer des éléments d'un dict par valeur.


7

Je construisais une liste de clés à supprimer, puis je les supprimais. C'est simple, efficace et évite tout problème lié à l'itération et à la mutation simultanées du dict.

keys_to_remove = [key for key, value in some_dict.iteritems()
                  if value == value_to_remove]
for key in keys_to_remove:
    del some_dict[key]

OP a demandé quand la clé est inconnue. Cette réponse suppose que la clé est connue.
Jean-François Corbett


1
y={'username':'admin','machine':['a','b','c']}
if 'c' in y['machine'] : del y['machine'][y['machine'].index('c')]

0

Il n'y a rien de mal à supprimer des éléments du dictionnaire lors de l'itération, comme vous l'avez proposé. Soyez prudent lorsque plusieurs threads utilisent le même dictionnaire en même temps, ce qui peut entraîner une KeyError ou d'autres problèmes.

Bien sûr, consultez la documentation sur http://docs.python.org/library/stdtypes.html#typesmapping


for k,v in d.iteritems(): del d[k]donnerait RuntimeError: dictionary changed size during iteration. Voir l'explication de mithrandi.
Buttons840

1
Bien sûr, d.iteritems () n'est pas la façon dont l'affiche originale itère, ni ce à quoi je faisais référence dans ma réponse.
Thane Anthem

0

Voilà comment je le ferais.

for key in some_dict.keys():
    if some_dict[key] == item_to_remove:
        some_dict.pop(key)
        break
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.