2010-09-17 10 views
28

Duplicar posibles:
'has_key()' or 'in'?determinar si una llave está presente en un diccionario

tengo un diccionario de Python como:

mydict = {'name':'abc','city':'xyz','country','def'} 

Quiero comprobar si un la clave está en el diccionario o no. Estoy ansioso por saber qué es más preferible de los siguientes dos casos y por qué?

1> if mydict.has_key('name'): 
2> if 'name' in mydict: 
+5

Por cierto, 'dict 'es el nombre de un tipo de Python incorporado así que es mejor evitar usarlo como nombre de variable en sus scripts (aunque estrictamente hablando, es legal hacerlo). – martineau

+4

[los documentos están bastante claros] (http://docs.python.org/library/stdtypes.html#dict.has_key), ¿no? – SilentGhost

+0

En Python 3, los objetos 'dict' ya no tienen el método' has_key() ', por lo que el operador' in' es mejor. – martineau

Respuesta

42
if 'name' in mydict: 

es la versión preferida, Pythonic. Se desaconseja el uso de has_key(), y este método has been removed in Python 3.

+2

Además, '" name "en dict' funcionará con diccionarios iterables y no solo. –

+2

¿Qué pasa con 'dict.get (clave)'? Eso también debería ser evitado? – user225312

+7

@PulpFiction: 'dict.get (clave)' puede ser útil cuando (1) no quiere un 'KeyError' en caso de que' key' no esté en dict (2) quiera usar un valor predeterminado si hay no es 'clave' (' dict.get (clave, predeterminado) '). El punto # 2 se puede hacer usando 'defaultdict' también. –

13

En términos de bytecode, in guarda un LOAD_ATTR y reemplaza un CALL_FUNCTION con un COMPARE_OP.

>>> dis.dis(indict) 
    2   0 LOAD_GLOBAL    0 (name) 
       3 LOAD_GLOBAL    1 (d) 
       6 COMPARE_OP    6 (in) 
       9 POP_TOP    


>>> dis.dis(haskey) 
    2   0 LOAD_GLOBAL    0 (d) 
       3 LOAD_ATTR    1 (haskey) 
       6 LOAD_GLOBAL    2 (name) 
       9 CALL_FUNCTION   1 
      12 POP_TOP    

Mis sentimientos son que es in mucho más fácil de leer y es preferible en todos los casos que se me ocurre.

En términos de rendimiento, el momento refleja el código de operación

$ python -mtimeit -s'd = dict((i, i) for i in range(10000))' "'foo' in d" 
10000000 loops, best of 3: 0.11 usec per loop 

$ python -mtimeit -s'd = dict((i, i) for i in range(10000))' "d.has_key('foo')" 
    1000000 loops, best of 3: 0.205 usec per loop 

in es casi dos veces más rápido.

+1

Cualquier medida de velocidad es, por supuesto, un problema específico, generalmente irrelevante, dependiente de la implementación, potencialmente dependiente de la versión y menos importante que los problemas de desaprobación y estilo. –

+2

@Mike Graham, en su mayoría tiene razón. Sí, pegué peor caso allí pensado, porque, IMO, ahí es donde realmente quieres saber. Además, creo que tu actitud es (aunque sigue siendo absolutamente correcta) un poco más apropiada para un lenguaje como C, en el que es rápido de cualquier forma a menos que realmente estropees algo. En Python vale la pena hacerlo bien en mayor medida. Además, los desarrolladores centrales tienen una forma de ajustar "la única forma correcta" de hacer algo para que, de nuevo, el rendimiento sea un buen indicador del buen estilo en mayor medida que lo normal en un idioma. – aaronasterling

7

Mi respuesta es "ninguno".

Creo que la forma más "pitonica" de hacer las cosas es NO verificar de antemano si la clave está en un diccionario y en su lugar solo escribir código que asume que está allí y atrapar cualquier KeyErrors que se active porque no fue así.

Esto se hace generalmente con el que encierra el código en una cláusula try...except y es un lenguaje conocido generalmente se expresa como "Es más fácil pedir perdón que permiso" o con las siglas EAFP, lo que básicamente significa que es mejor intentar algo y detectar los errores en su lugar para asegurarse de que todo esté bien antes de hacer cualquier cosa. ¿Por qué validar lo que no necesita ser validado cuando puede manejar las excepciones correctamente en lugar de tratar de evitarlas? Porque a menudo es más legible y el código tiende a ser más rápido si la probabilidad es baja de que la clave no esté allí (o las condiciones previas que pueda haber).

Por supuesto, esto no es apropiado en todas las situaciones y no todos están de acuerdo con la filosofía, por lo que tendrá que decidir por sí mismo caso por caso. No es sorprendente que lo contrario de esto se llama LBYL para "Look Before You Leap".

Como un ejemplo trivial tener en cuenta:

if 'name' in dct: 
    value = dct['name'] * 3 
else: 
    logerror('"%s" not found in dictionary, using default' % name) 
    value = 42 

vs

try: 
    value = dct['name'] * 3 
except KeyError: 
    logerror('"%s" not found in dictionary, using default' % name) 
    value = 42 

Aunque en el caso que es casi exactamente la misma cantidad de código, la segunda no pasa tiempo comprobando primero y es, probablemente, un poco más rápido por eso (intente ... excepto que el bloqueo no es totalmente gratis, por lo que probablemente no haga mucha diferencia aquí).

En general, las pruebas con anticipación a menudo pueden ser mucho más complicadas y el ahorro de no hacerlo puede ser significativo. Dicho esto, if 'name' in dict: es mejor por las razones indicadas en las otras respuestas.

Si usted está interesado en el tema, esta message titulado "EAFP vs LBYL (fue re: Un poco decepcionado hasta ahora)" del archivo de la lista de Python probablemente explica la diferencia entre los dos se acercó mejor que yo tener aquí. También hay una buena discusión sobre los dos enfoques en el libro Python in a Nutshell, 2nd Ed de Alex Martelli en el capítulo sobre excepciones titulado "Error-Checking Strategies".

+1

¿Hay datos que respalden la afirmación de que "la ganancia de ahorro por no hacerlo puede ser significativa"? Como desarrollador de Java, estoy acostumbrado a pensar que las excepciones son costosas y deberían ser para situaciones verdaderamente excepcionales. Su recomendación suena como "excepción como goto". ¿Puedes citar una fuente? – duffymo

+0

@duffymo. No, no puedo citar una fuente. Formulé la declaración porque, por lo general, hay muchas formas en que algo puede salir mal, un número relativamente pequeño de formas correctas. La comprobación de todas las formas incorrectas puede implicar una gran cantidad de código (que a menudo también es tedioso escribir). El manejo de excepciones puede ser lento en algunos idiomas y mencioné específicamente que esta podría no ser una buena forma de hacerlo si espera que ocurran con frecuencia. Tampoco estoy abogando por el uso de excepciones como parte del flujo de control normal o regular de un programa; deberían usarse, como usted dice, para circunstancias excepcionales. – martineau

+4

Las excepciones en Python son costosas. Si espera que la clave falte más de un pequeño porcentaje del tiempo, el costo de la excepción probablemente domine el tiempo de ejecución de la función. –

18

En la misma línea que la respuesta de Martineau, la mejor solución a menudo es no verificar. Por ejemplo, el código

if x in d: 
    foo = d[x] 
else: 
    foo = bar 

se escribe normalmente

foo = d.get(x, bar) 

que es más corto y más directamente habla de lo que quiere decir.

Otro caso común es algo así como

if x not in d: 
    d[x] = [] 

d[x].append(foo) 

que puede ser reescrito

d.setdefault(x, []).append(foo) 

o reescrito aún mejor mediante el uso de un collections.defaultdict(list) para d y escribir

d[x].append(foo) 
+0

Sí , algo que podríamos llamar "valores predeterminados inteligentes" (o incluso "diseño inteligente" ;-) – martineau

+3

Naw, estos son métodos y tipos que Python ha evolucionado con el tiempo. ';)' –

+0

¡Dios mío, tienes razón! – martineau

Cuestiones relacionadas