2010-09-09 14 views
5

¿Cómo:¿Cómo se compara el rendimiento de las búsquedas de claves del diccionario en Python?

dict = {} 
if key not in dict: 
dict[key] = foo 

Compare con:

try: 
dict[key] 
except KeyError: 
dict[key] = foo 

es decir, es el aspecto de una llave de ninguna manera más rápida que la búsqueda lineal a través dict.keys(), que supongo que la primera forma va a hacer?

+2

También existe el método dict.setdefault: http://docs.python.org/release/2.6.6/library/stdtypes.html#mapping-types-dict – GWW

+10

El primero ** no ** hace un lineal buscar Como dijo Larry Wall: "Hacer escaneos lineales sobre una matriz asociativa es como tratar de matar a alguien con una Uzi cargada". 'dict .__ contains__' hace aproximadamente lo mismo que los primeros 2/3 de' dict.__getitem__' (una búsqueda hash). – delnan

+3

Esa es una gran cita. – nmichaels

Respuesta

4

La respuesta depende de la frecuencia con la tecla ya está en el dict (Por cierto, ¿alguien ha mencionado a lo mal que una idea es ocultar una orden interna como dict detrás de una variable?)

if key not in dct: 
dct[key] = foo 

Si la clave está en el diccionario, esta hace una búsqueda de diccionario. Si la clave está en el diccionario, busca el diccionario dos veces.

try: 
dct[key] 
except KeyError: 
dct[key] = foo 

Esto puede ser un poco más rápido para el caso en que la clave está en el diccionario, pero lanzar una excepción tiene todo un gran sobrecarga, por lo que no es casi siempre la mejor opción.

dct.setdefault(key, foo) 

Ésta es un poco difícil: siempre implica dos búsquedas de diccionario: el primero es encontrar el método setdefault en la clase dict, la segunda es la búsqueda de key en el objeto dct. Además, si foo es una expresión, se evaluará siempre, mientras que las opciones anteriores solo la evaluarán cuando sea necesario.

Consulte también collections.defaultdict. Esa es la solución más adecuada para una gran clase de situaciones como esta.

+1

Buen punto para usar 'dict' Cambié el nombre de la variable al escribir el ejemplo y no lo pensé. La clave clave generalmente no está en el dict. –

+0

Iré con collections.defaultdict, gracias por señalar eso. Parece pitónico, y un pelo más rápido que dict.setdefault() –

+0

try profiling bro – coleifer

-1

my_dict.get(key, foo) devuelve foo si la clave no está en my_dict. El valor predeterminado es Ninguno, por lo que my_dict.get(key) devolverá None si la clave no está en my_dict. La primera de tus opciones es mejor si solo quieres agregar la clave a tu diccionario. No te preocupes por la velocidad aquí. Si encuentra que llenar su diccionario es un punto caliente en su programa, entonces piénselo. Pero no lo es. Entonces no.

+0

+1 - Muy pitónico. – duffymo

+1

Eso no establece el valor si no se establece al mirar su código, parece que está comprobando si la clave existe y configurándola de otra manera. – GWW

+0

@GWW: cierto. Sin embargo, podría usar 'dict [key] = dict.get (key, foo)'. – nmichaels

4

Probar: my_dict.setdefault(key, default). Sin embargo, es un poco más lento que las otras opciones.

Si key es en el diccionario, devuelva su valor. Si no, inserte key con un valor de default y devuelva default. default está predeterminado a Ninguno.

#!/usr/bin/env python 

example_dict = dict(zip(range(10), range(10))) 

def kn(key, d): 
    if key not in d: 
     d[key] = 'foo' 

def te(key, d): 
    try: 
     d[key] 
    except KeyError: 
     d[key] = 'foo' 

def sd(key, d): 
    d.setdefault(key, 'foo') 

if __name__ == '__main__': 
    from timeit import Timer 

    t = Timer("kn(2, example_dict)", "from __main__ import kn, example_dict") 
    print t.timeit() 
    t = Timer("te(2, example_dict)", "from __main__ import te, example_dict") 
    print t.timeit() 
    t = Timer("sd(2, example_dict)", "from __main__ import sd, example_dict") 
    print t.timeit() 

    # kn: 0.249855041504 
    # te: 0.244259119034 
    # sd: 0.375113964081 
+0

Es bastante interesante que el método integrado de python sea mucho más lento. – GWW

+0

Función de sobrecarga de llamada, supongo. – miku

+0

Y es interesante que con 'psyco.full()', las tres variantes solo toman alrededor del 10% del tiempo. – AndiDog

5

Usted está buscando el método setdefault:

>>> r = {} 
>>> r.setdefault('a', 'b') 
'b' 
>>> r 
{'a': 'b'} 
>>> r.setdefault('a', 'e') 
'b' 
>>> r 
{'a': 'b'} 
+0

+1 por ser el primero en leer la pregunta correctamente;) – delnan

5

sólo para aclarar un punto: if key not in d no hacer una búsqueda lineal a través de teclas d's. Utiliza la tabla hash de dict para encontrar rápidamente la clave.

+0

Exactamente lo que estoy tratando de descubrir, ¡ta! –

Cuestiones relacionadas