Vérifier si une clé donnée existe déjà dans un dictionnaire

2683

Je voulais tester si une clé existe dans un dictionnaire avant de mettre à jour la valeur de la clé. J'ai écrit le code suivant:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

Je pense que ce n'est pas la meilleure façon d'accomplir cette tâche. Existe-t-il une meilleure façon de tester une clé dans le dictionnaire?

Mohan Gulati
la source
31
L'appel dict.keys()crée une liste de clés, selon la documentation docs.python.org/2/library/stdtypes.html#dict.keys mais je serais surpris si ce modèle n'était pas optimisé pour, dans une implémentation sérieuse, à traduire à if 'key1' in dict:.
Evgeni Sergeev
7
Donc , j'ai finalement trouvé pourquoi beaucoup de mes scripts Python étaient si lents :) :(. C'est parce que j'ai utilisé x in dict.keys()pour vérifier les clés. Et cela est arrivé parce que la façon habituelle pour parcourir les clés de Java est for (Type k : dict.keySet()), cette habitude à l' origine for k in dict.keys()de se sentir plus naturel que for k in dict(ce qui devrait encore être bon en termes de performances?), mais vérifier les clés devient if k in dict.keys()aussi, ce qui est un problème ...
Evgeni Sergeev
4
@EvgeniSergeev if k in dict_:teste la présence de k dans les CLÉS de dict_, donc vous n'en avez toujours pas besoin dict_.keys(). (Cela m'a mordu, car il me semble que ses tests pour une valeur dans dict. Mais ce n'est pas le cas.)
ToolmakerSteve
1
@ToolmakerSteve C'est vrai, mais non seulement vous n'en avez pas besoin, mais ce n'est pas une bonne pratique.
Evgeni Sergeev
26
Essayez "key in dict"
marcelosalloum

Réponses:

3379

inest le moyen prévu pour tester l'existence d'une clé dans un fichier dict.

d = {"key1": 10, "key2": 23}

if "key1" in d:
    print("this will execute")

if "nonexistent key" in d:
    print("this will not")

Si vous vouliez une valeur par défaut, vous pouvez toujours utiliser dict.get():

d = dict()

for i in range(100):
    key = i % 10
    d[key] = d.get(key, 0) + 1

et si vous vouliez toujours garantir une valeur par défaut pour n'importe quelle clé, vous pouvez soit l'utiliser à dict.setdefault()plusieurs reprises ou à defaultdictpartir du collectionsmodule, comme ceci:

from collections import defaultdict

d = defaultdict(int)

for i in range(100):
    d[i % 10] += 1

mais en général, le inmot-clé est le meilleur moyen de le faire.

Chris B.
la source
74
J'utilise généralement juste getsi je veux retirer l'élément du dictionnaire de toute façon. Cela n'a aucun sens d'utiliser in et de retirer l'élément du dictionnaire.
Jason Baker,
75
Je suis complètement d'accord. Mais si vous avez seulement besoin de savoir si une clé existe, ou si vous devez faire la distinction entre un cas où la clé est définie et un cas où vous utilisez une valeur par défaut, inc'est la meilleure façon de le faire.
Chris B.21
5
La référence pour cette réponse est dans les documents python
enkash
30
get est un mauvais test si la clé est équivalente à "False", comme 0par exemple. J'ai appris ça à la dure: /
Sebastien
4
Je ne peux pas convenir qu'il s'agit d'une réponse complète car elle ne mentionne pas que «essayer» - «sauf» sera le plus rapide lorsque le nombre de clés échoue est suffisamment petit. Voir cette réponse ci-dessous: stackoverflow.com/a/1602945/4376643
Craig Hicks
1547

Vous n'avez pas besoin d'appeler les touches:

if 'key1' in dict:
  print("blah")
else:
  print("boo")

Cela sera beaucoup plus rapide car il utilise le hachage du dictionnaire plutôt que de faire une recherche linéaire, ce que feraient les touches d'appel.

Jason Baker
la source
7
C'est super. J'avais l'impression qu'il parcourrait en interne la liste des clés, mais je vois que cela fonctionne plus comme tester l'appartenance à un ensemble.
Mohan Gulati
51
@ Mohan Gulati: Vous comprenez qu'un dictionnaire est une table de hachage de clés mappées à des valeurs, non? Un algorithme de hachage convertit la clé en un entier et l'entier est utilisé pour trouver un emplacement dans la table de hachage qui correspond. en.wikipedia.org/wiki/Hash_table
hughdbrown
5
@Charles Addis, grâce à son expérience de travail avec environ un demi-million de clés, vous obtenez au moins 10 fois plus de performances lorsque vous écrivez "clé dans dict" au lieu de "clé dans dict.keys ()". PEP et Zen indiquent également que vous devez les ignorer au cas où ils seraient mauvais pour votre projet.
ivan_bilan
11
ivan_bilan - Je viens de lancer mon propre test sur ce point ... Sur un demi-million de clés, cela a if key in d1pris 0.17265701293945312quelques secondes. L'appel a if key in d1.keys()pris 0.23871088027954102- c'est la définition classique d'une micro-optimisation. Gagner des 0.07884883880615234secondes n'est pas une amélioration des performances.
Charles Addis
11
@Eli Juste pour vous, j'ai créé un test que vous pouvez exécuter vous-même. Les résultats peuvent vous étonner. Pour les dict avec ~ 50 000 clés, ne pas appeler keys()vous offre un avantage de calcul de 0,01 seconde. Pour ~ 500 000 clés, le fait de ne pas appeler keys()vous donne 0,1 seconde avantage. Pour ~ 5 000 000 touches, ne pas appeler keys()est 0,4 seconde plus rapide, mais pour 50 000 000 touches, APPELER keys()EST 3 SECONDES PLUS RAPIDE!
Charles Addis
268

Vous pouvez tester la présence d'une clé dans un dictionnaire à l'aide du mot clé in :

d = {'a': 1, 'b': 2}
'a' in d # <== evaluates to True
'c' in d # <== evaluates to False

Une utilisation courante pour vérifier l'existence d'une clé dans un dictionnaire avant de la muter est d'initialiser par défaut la valeur (par exemple, si vos valeurs sont des listes, par exemple, et que vous voulez vous assurer qu'il existe une liste vide à laquelle vous pouvez ajouter lors de l'insertion de la première valeur d'une clé). Dans de tels cas, vous pouvez trouver le collections.defaultdict()type d'intérêt.

Dans un code plus ancien, vous pouvez également trouver certaines utilisations de has_key(), une méthode obsolète pour vérifier l'existence de clés dans les dictionnaires (utilisez simplement à la key_name in dict_nameplace).

Michael Aaron Safyan
la source
2
Je voulais partager que (en utilisant Python 2.7) le temps d'exécution de quelque chose que je viens d'écrire, en se basant fortement sur les dictés, était de 363.235070 en utilisant "key in dict.keys ()" et est descendu de façon drastique à 0.260186 uniquement en supprimant l'appel à "keys ( ) "
Ido_f
@Ido_f veuillez poster vos benchmarks, car mes benchmarks n'ont pratiquement aucune différence en 3.5 et 2.7
Charles Addis
@Ido_f Je soupçonne que c'était quelque chose d'autre dans votre programme qui était autre chose, mais pas vraiment key in dict.keys(). Essayez de supprimer tout le code à l'exception de cette vérification et voyez quel est votre résultat.
Charles Addis
101

Vous pouvez raccourcir ceci:

if 'key1' in dict:
    ...

Cependant, c'est au mieux une amélioration cosmétique. Pourquoi pensez-vous que ce n'est pas la meilleure façon?

Greg Hewgill
la source
100
C'est bien plus qu'une amélioration cosmétique. Le temps pour trouver une clé en utilisant cette méthode est O (1) alors que les clés appelantes généreraient une liste et seraient O (n).
Jason Baker,
5
Le O (1) ne semble pas tout à fait raison. Êtes-vous sûr que ce n'est pas quelque chose comme O (log n)?
spectras
12
C'est la complexité d'une recherche de dict unique, qui est en moyenne O (1) et au pire O (n). .list () sera toujours O (n). wiki.python.org/moin/TimeComplexity
Leonora Tindall
1
cela évite également une allocation supplémentaire. (important pour faire des boucles serrées un peu plus rapidement)
nurettin
57

Pour plus d'informations sur la vitesse d'exécution des méthodes proposées pour la réponse acceptée (boucles de 10 m):

  • 'key' in mydict temps écoulé 1,07 sec
  • mydict.get('key') temps écoulé 1,84 sec
  • mydefaultdict['key'] temps écoulé 1,07 sec

Par conséquent, l'utilisation inou defaultdictest déconseillée get.

Wtower
la source
6
tout à fait d'accord que get1,84 s est <1,07 * 2 ;-P
Paul Rigor
54

Je recommanderais setdefaultplutôt d' utiliser la méthode. On dirait qu'il fera tout ce que vous voulez.

>>> d = {'foo':'bar'}
>>> q = d.setdefault('foo','baz') #Do not override the existing key
>>> print q #The value takes what was originally in the dictionary
bar
>>> print d
{'foo': 'bar'}
>>> r = d.setdefault('baz',18) #baz was never in the dictionary
>>> print r #Now r has the value supplied above
18
>>> print d #The dictionary's been updated
{'foo': 'bar', 'baz': 18}
David Berger
la source
9
Qu'est-ce que cela setdefaulta à voir avec la question du PO?
Hughdbrown
18
@hughdbrown "Je voulais tester si une clé existe dans un dictionnaire avant de mettre à jour la valeur de la clé." Parfois, les publications incluent du code qui génère une vague de réponses à quelque chose qui n'est pas tout à fait l'objectif initial. Pour atteindre l'objectif indiqué dans la première phrase, setdefault est la méthode la plus efficace, même s'il ne s'agit pas d'un remplacement direct de l'exemple de code publié.
David Berger
5
C'est la réponse supérieure car elle satisfait l'objectif d'OP au lieu de simplement donner la réponse techniquement correcte. Voir: nedbatchelder.com/blog/201207/…
Niels Bom
+1 pour une réponse informative, cela m'a appris quelque chose. Cependant, si c'est la meilleure solution dépend de ce que le codeur a en tête; par exemple la signification de "avant de mettre à jour la valeur de la clé". Peut-être qu'il va lever une exception si elle n'est pas présente (== pas d'autorisation d'ajouter de nouvelles clés). Peut-être que c'est un dictionnaire de comptes, et il va ajouter 1 au compte existant, auquel cas `d [clé] = d.get (clé, 0) + 1 'est la solution la plus propre (comme Chris le montre, après votre réponse était écrit). (Je prends la peine de mentionner cela, au cas où de futurs lecteurs viendraient ici, avec différentes tâches à l'esprit.)
ToolmakerSteve
1
@ToolmakerSteve True. Le problème ici est que la question de OP n'était pas assez claire.
Niels Bom
45

Le dictionnaire en python a une méthode get ('clé', par défaut). Vous pouvez donc simplement définir une valeur par défaut au cas où il n'y aurait pas de clé.

values = {...}
myValue = values.get('Key', None)
mafonya
la source
33

Qu'en est-il de l'utilisation d'EAFP (plus facile de demander pardon que permission):

try:
   blah = dict["mykey"]
   # key exists in dict
except KeyError:
   # key doesn't exist in dict

Voir d'autres postes SO:

Utiliser try vs if en python ou

Vérification de l'existence de membres en Python

HungryArthur
la source
12
Essayez / excepté peut être plus cher s'il est probable que la clé n'existe pas souvent. Du post que vous avez référencé: "[I] si vous vous attendez à ce que le résultat à 99% du temps contienne réellement quelque chose d'itérable, j'utiliserais l'approche try / except. Ce sera plus rapide si les exceptions sont vraiment exceptionnelles. Si le résultat est None plus de 50% du temps, alors l'utilisation de if est probablement meilleure. [...] [A] n si l'instruction vous coûte toujours, il est presque libre de configurer un bloc try / except. Mais quand une exception se produit, le le coût est beaucoup plus élevé. " stackoverflow.com/a/1835844/1094092
billrichards
28

En utilisant l'opérateur ternaire:

message = "blah" if 'key1' in dict else "booh"
print(message)
Charitoo
la source
20

Les moyens d'obtenir les résultats sont les suivants:

Ce qui est mieux dépend de 3 choses:

  1. Le dictionnaire «a-t-il normalement la clé» ou «n'a-t-il pas normalement la clé»?
  2. Avez-vous l'intention d'utiliser des conditions comme si ... sinon ... elseif ... sinon?
  3. Quelle est la taille du dictionnaire?

En savoir plus: http://paltman.com/try-except-performance-in-python-a-simple-test/

Utilisation de try / block au lieu de 'in' ou 'if':

try:
    my_dict_of_items[key_i_want_to_check]
except KeyError:
    # Do the operation you wanted to do for "key not present in dict".
else:
    # Do the operation you wanted to do with "key present in dict."
Bishwas Mishra
la source
2
Bon mais doit être actualisé pour python 3. J'ai converti le script de la page Web avec 2to3, et j'ai vu que la syntaxe sans try est toujours plus rapide que la syntaxe avec try, même dans le cas où la clé est dans le dict.
Jean Paul
18

Python 2 uniquement: (et python 2.7 prend indéjà en charge )

vous pouvez utiliser la méthode has_key ():

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass
wan kenobi
la source
22
.has_key()a été dépréciée ; vous devez utiliser incomme indiqué dans les autres réponses.
Brad Koch
12
BTW, je recommande de lire TOUTES les réponses existantes à une VIEILLE question, avant d'y répondre. Cette réponse n'a rien ajouté, puisque la suggestion existait déjà dans la réponse de Michael, à partir de '09. (Je ne veux pas décourager une tentative d'ajouter quelque chose d'utile à une discussion. Continuez à essayer.)
ToolmakerSteve
16

Juste une info pour Chris. B (meilleure réponse):

d = defaultdict(int)

Fonctionne également; la raison en est que l'appel int()renvoie 0ce qui se defaultdictpasse en arrière-plan (lors de la construction d'un dictionnaire), d'où le nom "Factory Function" dans la documentation.

Mauricio Morales
la source
2
Si vous créez un dictionnaire de décomptes, vous devez utiliser Counter (en supposant Python 2.7). Et j'ai utilisé defaultdict(lambda: 0)au lieu de defaultdict(int)parce que je pense que c'est plus clair ce qui se passe; le lecteur n'a pas besoin de savoir 0si vous appelez int()sans argument. YMMV.
Chris
9

Vérifier si une clé donnée existe déjà dans un dictionnaire

Pour avoir une idée de comment faire cela, nous inspectons d'abord les méthodes que nous pouvons appeler dans le dictionnaire. Voici les méthodes:

d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Python Dictionary clear()       Removes all Items
Python Dictionary copy()        Returns Shallow Copy of a Dictionary
Python Dictionary fromkeys()    Creates dictionary from given sequence
Python Dictionary get()         Returns Value of The Key
Python Dictionary items()       Returns view of dictionary (key, value) pair
Python Dictionary keys()        Returns View Object of All Keys
Python Dictionary pop()         Removes and returns element having given key
Python Dictionary popitem()     Returns & Removes Element From Dictionary
Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
Python Dictionary update()      Updates the Dictionary 
Python Dictionary values()      Returns view of all values in dictionary

La méthode brutale pour vérifier si la clé existe déjà peut être la get()méthode:

d.get("key")

Les deux autres méthodes intéressantesitems() et keys()semblent trop de travail. Examinons donc si get()c'est la bonne méthode pour nous. Nous avons notre dict d:

d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

L'impression montre que la clé que nous n'avons pas sera renvoyée None:

print(d.get('key')) #None
print(d.get('clear')) #0
print(d.get('copy')) #1

Nous pouvons l' utiliser pour obtenir l'information si la clé est présente ou non. Mais considérez ceci si nous créons un dict avec un seul key:None:

d= {'key':None}
print(d.get('key')) #None
print(d.get('key2')) #None

Diriger cette get()méthode n'est pas fiable au cas où certaines valeurs pourraient l'être None. Cette histoire devrait avoir une fin plus heureuse. Si nous utilisons le incomparateur:

print('key' in d) #True
print('key2' in d) #False

Nous obtenons les bons résultats. Nous pouvons examiner le code d'octet Python:

import dis
dis.dis("'key' in d")
#   1           0 LOAD_CONST               0 ('key')
#               2 LOAD_NAME                0 (d)
#               4 COMPARE_OP               6 (in)
#               6 RETURN_VALUE

dis.dis("d.get('key2')")
#   1           0 LOAD_NAME                0 (d)
#               2 LOAD_METHOD              1 (get)
#               4 LOAD_CONST               0 ('key2')
#               6 CALL_METHOD              1
#               8 RETURN_VALUE

Cela montre que l' inopérateur de comparaison n'est pas seulement plus fiable mais encore plus rapide que get().

prosti
la source
.get()peut avoir un deuxième argument pour la defaultvaleur, qui pourrait être utilisé pour gérer le problème où key:None. exemple: d.get("key", False)
Alex
.get()est le moyen le plus rapide. Une autre option consiste à affecter dans un bloc try/except
HCLivess
7

Le dictionnaire Python a la méthode appelée __contains__. Cette méthode retournera True si le dictionnaire a la clé sinon renvoie False.

 >>> temp = {}

 >>> help(temp.__contains__)

Help on built-in function __contains__:

__contains__(key, /) method of builtins.dict instance
    True if D has a key k, else False.
Siva Gnanam
la source
2
C'est une très mauvaise pratique d'appeler __contains__directement. La façon correcte de le faire est d'utiliser l' inopérateur, qui est celui containment checkqui invoque la __contains__fonction.
user1767754
@ user1767754 J'utilise foo = x['foo'] if x.__contains__('foo') else 'bar'. Des idées sur la façon dont pourrait utiliser l' inopérateur dans le cadre de cette expression?
donrondadon
1
foo = x['foo'] if 'foo' in x else 'bar'
Ray Wu
5

Partager une autre façon de vérifier si une clé existe à l'aide d'opérateurs booléens.

d = {'a': 1, 'b':2}
keys = 'abcd'

for k in keys:
    x = (k in d and 'blah') or 'boo'
    print(x) 

Cela revient

>>> blah
>>> blah
>>> boo
>>> boo

Explication

D' abord , vous devez savoir qu'en Python, 0, Noneou des objets avec une longueur nulle à évaluer False. Tout le reste est évalué True. Les opérations booléennes sont évaluées de gauche à droite et renvoient l'opérande non True ou False.

Voyons un exemple:

>>> 'Some string' or 1/0 
'Some string'
>>>

Puisque 'Some string'évalue à True, le reste de orn'est pas évalué et il n'y a pas de division par zéro d'erreur levée.

Mais si nous passons la commande 1/0est évaluée en premier et déclenche une exception:

>>> 1/0 or 'Some string'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 

Nous pouvons l'utiliser pour le modèle pour vérifier si une clé existe.

(k in d and 'blah')

fait la même chose que

if k in d:
    'blah'
else:
    False

Cela renvoie déjà le résultat correct si la clé existe, mais nous voulons qu'elle affiche «boo» lorsqu'elle ne l'est pas. Donc, nous prenons le résultat et oravec'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 
J. Antunes
la source
1

Vous pouvez utiliser la forboucle pour parcourir le dictionnaire et obtenir le nom de la clé que vous souhaitez trouver dans le dictionnaire, après cela, vérifiez si elle existe ou n'utilise pas la ifcondition:

dic = {'first' : 12, 'second' : 123}
for each in dic:
    if each == 'second': 
        print('the key exists and the corresponding value can be updated in the dictionary')
Abdulkalek
la source
vérifier le code parce que la sortie pour cela est it is existetnot exist
system123456
pourquoi utiliser un dictionnaire pour effectuer une recherche linéaire?
Jean-François Fabre