Compruebe si ya existe una clave determinada en un diccionario

2683

Quería probar si existe una clave en un diccionario antes de actualizar el valor de la clave. Escribí el siguiente código:

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

Creo que esta no es la mejor manera de lograr esta tarea. ¿Hay una mejor manera de probar una clave en el diccionario?

Mohan Gulati
fuente
31
Las llamadas dict.keys()crean una lista de claves, de acuerdo con la documentación docs.python.org/2/library/stdtypes.html#dict.keys, pero me sorprendería si este patrón no estuviera optimizado para, en una implementación seria, traducir a if 'key1' in dict:.
Evgeni Sergeev
77
Así que finalmente descubrí por qué muchos de mis scripts de Python eran tan lento :) :(. Esto se debe a que he estado utilizando x in dict.keys()para comprobar si hay llaves. Y eso sucedió porque la forma habitual para iterar sobre las claves de Java está for (Type k : dict.keySet()), este hábito causando for k in dict.keys()a se siente más natural que for k in dict(¿qué debería estar bien en términos de rendimiento?), pero luego comprobar las teclas if k in dict.keys()también se vuelve , lo cual es un problema ...
Evgeni Sergeev
44
@EvgeniSergeev if k in dict_:prueba la presencia de k en las CLAVES de dict_, por lo que aún no es necesario dict_.keys(). (Esto me ha mordido, ya que me dice que está probando un valor en dict. Pero no lo es.)
ToolmakerSteve
1
@ToolmakerSteve Eso es correcto, pero no solo no lo necesitas, no es una buena práctica.
Evgeni Sergeev
26
Prueba "key in dict"
marcelosalloum

Respuestas:

3375

ines la forma prevista para probar la existencia de una clave en a dict.

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

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

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

Si desea un valor predeterminado, siempre puede usar dict.get():

d = dict()

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

y si siempre quisiste asegurar un valor predeterminado para cualquier clave que puedas usar dict.setdefault()repetidamente o defaultdictdesde el collectionsmódulo, así:

from collections import defaultdict

d = defaultdict(int)

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

pero en general, la inpalabra clave es la mejor manera de hacerlo.

Chris B.
fuente
74
Por lo general, solo uso getsi voy a sacar el elemento del diccionario de todos modos. No tiene sentido usar in y sacar el elemento del diccionario.
Jason Baker
75
Totalmente de acuerdo. Pero si solo necesita saber si existe una clave, o si necesita distinguir entre un caso en el que la clave está definida y un caso en el que está utilizando un valor predeterminado, ines la mejor manera de hacerlo.
Chris B.
55
La referencia para esta respuesta está en los documentos de Python
enkash el
30
get es una mala prueba si la clave es equivalente a "False", como 0por ejemplo. Aprendí esto de la manera difícil: /
Sebastien
44
No puedo estar de acuerdo con que esta sea una respuesta completa, ya que no menciona que 'probar' - 'excepto' será el más rápido cuando el número de fallas clave sea lo suficientemente pequeño. Vea esta respuesta a continuación: stackoverflow.com/a/1602945/4376643
Craig Hicks
1547

No tienes que llamar a las teclas:

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

Eso será mucho más rápido ya que utiliza el hashing del diccionario en lugar de hacer una búsqueda lineal, lo que haría las teclas de llamada.

Jason Baker
fuente
77
Eso es genial Tenía la impresión de que internamente todavía atravesaría la lista de claves, pero veo que esto funciona más como probar la membresía en un conjunto.
Mohan Gulati
51
@ Mohan Gulati: Entiendes que un diccionario es una tabla hash de claves asignadas a valores, ¿verdad? Un algoritmo hash convierte la clave en un entero y el entero se usa para encontrar una ubicación en la tabla hash que coincida. es.wikipedia.org/wiki/Hash_table
hughdbrown
55
@Charles Addis, por experiencia trabajando con alrededor de medio millón de teclas, obtienes un aumento de rendimiento de al menos 10 veces al escribir "key in dict" en lugar de "key in dict.keys ()". PEP y Zen también establece que debe ignorarlos en caso de que sean perjudiciales para su proyecto.
ivan_bilan
11
ivan_bilan - Me acabo de encontrar mi propia benchtest en este ... En medio millón de llaves, if key in d1tomó 0.17265701293945312segundos. Llamadas if key in d1.keys()tomadas 0.23871088027954102: esta es la definición clásica de una microoptimización. Ahorrar 0.07884883880615234segundos no es un aumento de rendimiento.
Charles Addis
11
@Eli Solo para ti, he creado una prueba que puedes ejecutar tú mismo. Los resultados pueden sorprenderte. Para los dictos con ~ 50,000 teclas, no llamar keys()le brinda un beneficio computacional de .01 segundo Para ~ 500,000 llaves, no llamar keys()le brinda .1 segundo beneficio. Para ~ 5,000,000 llaves, no llamar keys()es .4 segundos más rápido, pero para 50,000,000 llaves ¡ LLAMAR keys()ES 3 SEGUNDOS MÁS RÁPIDO!
Charles Addis
268

Puede probar la presencia de una clave en un diccionario, utilizando la palabra clave in :

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

Un uso común para verificar la existencia de una clave en un diccionario antes de mutarlo es inicializar el valor por defecto (por ejemplo, si sus valores son listas, por ejemplo, y desea asegurarse de que haya una lista vacía a la que pueda agregar al insertar el primer valor para una clave). En casos como esos, puede encontrar el collections.defaultdict()tipo de interés.

En el código anterior, también puede encontrar algunos usos de has_key(), un método obsoleto para verificar la existencia de claves en los diccionarios (solo use key_name in dict_name, en su lugar).

Michael Aaron Safyan
fuente
2
Quería compartir que (usando Python 2.7) el tiempo de ejecución de algo que acabo de escribir, basándome en gran medida en los dictados, fue 363.235070 usando "key in dict.keys ()" y bajó drásticamente a 0.260186 únicamente al eliminar la llamada para "keys ( ) "
Ido_f
@Ido_f por favor publique sus puntos de referencia, ya que mis puntos de referencia casi no tienen diferencias en 3.5 y 2.7
Charles Addis
@Ido_f Sospecho que era otra cosa en su programa que era otra cosa, pero en realidad no key in dict.keys(). Intente eliminar todo el código excepto este control y vea cuál es su resultado.
Charles Addis
101

Puedes acortar esto:

if 'key1' in dict:
    ...

Sin embargo, esto es, en el mejor de los casos, una mejora cosmética. ¿Por qué crees que esta no es la mejor manera?

Greg Hewgill
fuente
100
Esto es mucho más que una mejora cosmética. El tiempo para encontrar una clave usando este método es O (1) mientras que las claves de llamada generarían una lista y serían O (n).
Jason Baker
55
El O (1) no parece del todo correcto. ¿Estás seguro de que no es algo como O (log n)?
espectras
12
Es la complejidad de una sola búsqueda de dict, que es en promedio O (1) y en el peor de los casos O (n). .list () siempre será O (n). wiki.python.org/moin/TimeComplexity
Leonora Tindall
1
Esto también evita una asignación adicional. (importante para hacer bucles apretados un poco más rápido)
nurettin
57

Para obtener información adicional sobre la ejecución rápida de los métodos propuestos de la respuesta aceptada (bucles de 10 m):

  • 'key' in mydict tiempo transcurrido 1.07 segundos
  • mydict.get('key') tiempo transcurrido 1.84 segundos
  • mydefaultdict['key'] tiempo transcurrido 1.07 segundos

Por lo tanto, el uso ino defaultdictse recomiendan contra get.

Wtower
fuente
66
totalmente de acuerdo en que get1.84s es <1.07 * 2 ;-P
Paul Rigor
54

Recomendaría usar el setdefaultmétodo en su lugar. Parece que hará todo lo que quieras.

>>> 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
fuente
99
¿Qué setdefaulttiene que ver con la pregunta del OP?
hughdbrown
18
@hughdbrown "Quería probar si existe una clave en un diccionario antes de actualizar el valor de la clave". A veces, las publicaciones incluyen código que genera una serie de respuestas a algo que no es el objetivo original. Para lograr el objetivo establecido en la primera oración, setdefault es el método más efectivo, a pesar de que no es un reemplazo directo para el código de muestra publicado.
David Berger
55
Esta es la respuesta superior porque satisface el objetivo de OP en lugar de simplemente dar la respuesta técnicamente correcta. Ver: nedbatchelder.com/blog/201207/…
Niels Bom
+1 para una respuesta informativa, que me enseñó algo. Sin embargo, si es la mejor solución depende de lo que el codificador tenga en mente; por ejemplo, el significado de "antes de actualizar el valor de la clave". Quizás lanzará una excepción si no está presente (== sin permiso para agregar nuevas claves). Tal vez sea un diccionario de conteos, y él va a agregar 1 al conteo existente, en cuyo caso `d [clave] = d.get (clave, 0) + 1 'es la solución más limpia (como muestra Chris, después de su respuesta fue escrito). (Solo me molesto en mencionar esto, en caso de que futuros lectores vengan aquí, con diferentes tareas en mente.)
ToolmakerSteve
1
@ToolmakerSteve True. El problema aquí es que la pregunta de OP no fue lo suficientemente clara.
Niels Bom
45

El diccionario en python tiene un método get ('clave', predeterminado). Por lo tanto, puede establecer un valor predeterminado en caso de que no haya una clave.

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

¿Qué pasa con el uso de EAFP (más fácil pedir perdón que permiso):

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

Ver otras publicaciones SO:

Usar try vs if en python o

Comprobando la existencia de miembros en Python

HambreArthur
fuente
12
Probar / excepto puede ser más costoso si es probable que la clave a menudo no exista. De la publicación a la que hizo referencia: "[I] f si espera que el resultado del 99% del tiempo realmente contenga algo iterable, usaría el enfoque try / except. Será más rápido si las excepciones son realmente excepcionales. Si el resultado es Ninguno más del 50% del tiempo, luego usar if es probablemente mejor. [A] n si la declaración siempre le cuesta, es casi gratis configurar un bloque try / except. Pero cuando ocurre una Excepción, el el costo es mucho mayor ". stackoverflow.com/a/1835844/1094092
billrichards
28

Usando operador ternario:

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

Las formas en que puede obtener los resultados son:

Lo que es mejor depende de 3 cosas:

  1. ¿El diccionario "normalmente tiene la clave" o "normalmente no tiene la clave"?
  2. ¿Tiene la intención de utilizar condiciones como si ... más ... más si ... más?
  3. ¿Qué tan grande es el diccionario?

Lee mas: http://paltman.com/try-except-performance-in-python-a-simple-test/

Uso de try / block en lugar de 'in' o '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
fuente
2
Bueno, pero necesita actualizarse para Python 3. Convertí el script de la página web con 2to3, y vi que la sintaxis sin prueba siempre es más rápida que con la sintaxis con prueba, incluso en el caso de que la clave esté en el dict.
Jean Paul
18

Solo Python 2: (y Python 2.7 inya es compatible )

puedes usar el método has_key ():

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass
wan kenobi
fuente
22
.has_key()ha sido desaprobado ; debe usar incomo se muestra en otras respuestas.
Brad Koch
12
Por cierto, recomiendo leer TODAS las respuestas existentes a una pregunta ANTIGUA , antes de responderla. Esta respuesta no agregó nada, ya que la sugerencia ya existía en la respuesta de Michael, del '09. (No me refiero a desalentar un intento de agregar algo útil a una discusión. Siga intentándolo.)
ToolmakerSteve
16

Solo un FYI que agrega a Chris. B (mejor respuesta):

d = defaultdict(int)

También funciona; la razón es que la llamada int()devuelve, 0que es lo que defaultdicthace detrás de escena (cuando se construye un diccionario), de ahí el nombre "Función de fábrica" ​​en la documentación.

Mauricio Morales
fuente
2
Si está creando un diccionario de recuentos, debería usar Counter (suponiendo Python 2.7). Y lo usé en defaultdict(lambda: 0)lugar de hacerlo defaultdict(int)porque creo que está más claro lo que está sucediendo; el lector no necesita saber lo que obtienes 0si llamas int()sin argumentos. YMMV.
Chris B.
9

Compruebe si ya existe una clave determinada en un diccionario

Para tener la idea de cómo hacerlo, primero inspeccionamos qué métodos podemos llamar en el diccionario. Aquí están los métodos:

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

El método brutal para verificar si la clave ya existe puede ser el get()método:

d.get("key")

Los otros dos métodos y sonidos interesantes parecen demasiado trabajo. Así que examinemos si es el método correcto para nosotros. Tenemos nuestro dict :items()keys()get()d

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

La impresión muestra que la clave que no tenemos regresará None:

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

Nosotros podemos usar eso para obtener la información si la clave está presente o no. Pero considere esto si creamos un dict con un solo key:None:

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

Liderar ese get()método no es confiable en caso de que algunos valores lo sean None. Esta historia debería tener un final más feliz. Si usamos el incomparador:

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

Obtenemos los resultados correctos. Podemos examinar el código de bytes de 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

Esto muestra que el inoperador de comparación no solo es más confiable sino incluso más rápido que get().

prosti
fuente
.get()puede tener un segundo argumento para el defaultvalor, que podría usarse para manejar el problema donde key:None. ejemplo: d.get("key", False)
Alex
.get()Es la forma más rápida. Otra opción es asignar en un try/ exceptbloque
HCLivess
7

El diccionario de Python tiene el método llamado __contains__. Este método devolverá True si el diccionario tiene la clave; de ​​lo contrario, devuelve 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
fuente
2
Es una muy mala práctica llamar __contains__directamente. La forma correcta de hacerlo es utilizar el inoperador, que es el containment checkque invoca la __contains__función.
user1767754
@ user1767754 Estoy usando foo = x['foo'] if x.__contains__('foo') else 'bar'. ¿Alguna idea de cómo podría usar el inoperador como parte de esta expresión?
donrondadon
1
foo = x['foo'] if 'foo' in x else 'bar'
Ray Wu
5

Compartir una forma más de verificar si existe una clave utilizando operadores booleanos.

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

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

Esto vuelve

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

Explicación

En primer lugar usted debe saber que en Python, 0, None, o los objetos con longitud cero a evaluar False. Todo lo demás se evalúa a True. Las operaciones booleanas se evalúan de izquierda a derecha y devuelven el operando no verdadero o falso.

Veamos un ejemplo:

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

Como se 'Some string'evalúa como True, el resto del orno se evalúa y no se genera división por error cero.

Pero si cambiamos el orden 1/0se evalúa primero y se genera una excepción:

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

Podemos usar esto para el patrón para verificar si existe una clave.

(k in d and 'blah')

hace lo mismo que

if k in d:
    'blah'
else:
    False

Esto ya devuelve el resultado correcto si la clave existe, pero queremos que imprima 'boo' cuando no existe. Entonces, tomamos el resultado y orcon'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 
J. Antunes
fuente
1

Puede usar el forbucle para iterar sobre el diccionario y obtener el nombre de la clave que desea encontrar en el diccionario, luego verifique si existe o no con la ifcondición:

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
fuente
verifique el código porque la salida para esto es it is existynot exist
system123456
¿Por qué usar un diccionario si es para realizar una búsqueda lineal?
Jean-François Fabre