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?

Boutons840
la source
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.

Tim Pietzcker
la source
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}
N 1.1
la source
4
OP a demandé quand la clé est inconnue
nmz787
52
a = {'name': 'your_name','class': 4}
if 'name' in a: del a['name']
Kracekumar
la source
OP a demandé quand la clé est inconnue. Cette réponse suppose que la clé est connue.
Jean-François Corbett
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 () .

Luu Tuan Anh
la source
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.

mithrandi
la source
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]

la source
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')]
user3559640
la source
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

Hymne de Thane
la source
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
Nathan
la source