2010-04-12 5 views
48

Actualmente estoy volviendo a comprometerme con Python después de una larga ausencia y me encanta. Sin embargo, me encuentro encontrando un patrón una y otra vez. Sigo pensando que debe haber una mejor manera de expresar lo que quiero y que probablemente lo estoy haciendo mal.¿Cómo agregar o incrementar una entrada de diccionario?

El código que estoy escribiendo es de la siguiente forma:

# foo is a dictionary 
if foo.has_key(bar): 
    foo[bar] += 1 
else: 
    foo[bar] = 1 

estoy escribiendo esto mucho en mis programas. Mi primera reacción es llevarlo a una función auxiliar, pero a menudo las bibliotecas de Python ya ofrecen cosas como esta.

¿Hay algún pequeño truco de sintaxis que me falta? ¿O es así como debería hacerse?

+10

Como acotación al margen, se puede decir 'si foo bar en:' 'si en lugar de foo.has_key (bar):' – Cameron

+4

@Cameron : s/can/should/ – jfs

+0

@JF Sebastian: +1 para usar una expresión regular :-) – Cameron

Respuesta

85

Utilice un defaultdict:

from collections import defaultdict 

foo = defaultdict(int) 
foo[bar] += 1 

En Python> = 2.7, también tiene una clase separada Counter para estos fines. Para Python 2.5 y 2.6, puede usar su backported version.

+2

'collections.Counter' está en' 2.7' http://docs.python.org/dev/whatsnew/2.7.html#new-and-improved-modules – jfs

+0

Gracias, estoy arreglando la respuesta. –

+0

¡Gracias! No sabía sobre el incumplimiento. Eso es exactamente lo que estaba buscando. – cursa

2

Para Python> = 2.5 puede hacer lo siguiente:

foo[bar] = 1 if bar not in foo else foo[bar]+1 
+4

Si bien es válido, esto no es más conciso o legible que el código del OP. –

+0

Aunque no es conciso, es pitónico y legible. – sventechie

+0

@sventechie. Creo que puedes tener una definición muy personal de legible y pitónico. –

3

También puede tomar ventaja de la estructura de control en el manejo de excepciones. Un KeyError excepción es lanzada por un diccionario cuando intenta asignar un valor a una clave inexistente:

my_dict = {} 
try: 
    my_dict['a'] += 1 
except KeyError, err: # in 2.6: `except KeyError as err:` 
    my_dict['a'] = 1 
+7

El mero hecho de que el manejo de excepciones * pueda * ser utilizado para el flujo de control no significa que deba hacerlo. –

+0

AFAIK, haciendo algo así como dict.has_key (clave) en realidad intenta acceder a la clave y devuelve False si se detecta una excepción. – detly

78

get() El método dict 's toma un segundo parámetro opcional que se puede utilizar para proporcionar un valor predeterminado si la clave solicitada no se encuentra:

foo[bar] = foo.get(bar, 0) + 1 
+3

¿Por qué el voto a favor? Es válido y legible – Ponkadoodle

+0

No lo voté, pero supongo que el downvoter original lo hizo porque viola el principio DRY (No repetir): "foo" y "bar" se mencionan dos veces. –

+1

@Tamas: Bueno, la versión OPs menciona cada una de esas tres veces :) – truppo

4

Hice algunas comparaciones de tiempo. Más o menos igual. Sin embargo, el comando one-lined .get() es el más rápido.

Salida:

get 0.543551800627 
exception 0.587318710994 
haskey 0.598421703081 

Código:

import timeit 
import random 

RANDLIST = [random.randint(0, 1000) for i in range(10000)] 

def get(): 
    foo = {} 
    for bar in RANDLIST: 
     foo[bar] = foo.get(bar, 0) + 1 


def exception(): 
    foo = {} 
    for bar in RANDLIST: 
     try: 
      foo[bar] += 1 
     except KeyError: 
      foo[bar] = 1 


def haskey(): 
    foo = {} 
    for bar in RANDLIST: 
     if foo.has_key(bar): 
      foo[bar] += 1 
     else: 
      foo[bar] = 1 


def main(): 
    print 'get', timeit.timeit('get()', 'from __main__ import get', number=100) 
    print 'exception', timeit.timeit('exception()', 'from __main__ import exception', number=100) 
    print 'haskey', timeit.timeit('haskey()', 'from __main__ import haskey', number=100) 


if __name__ == '__main__': 
    main() 
+0

Interesante: me encanta ver algunas pruebas, ¡aunque las diferencias que has medido son mínimas! Me pregunto cómo se verían afectados al tener más o menos duplicados. Mi predicción: la versión de excepción funcionará mejor cuando foo [bar] + = 1 normalmente tenga éxito – Alex

+0

Aumentar los duplicados mejora el rendimiento del código de excepción. Cambiando RANDLIST a [random.randint (0, 100) para i en rango (10000)] producido: obtenga 0.0955109596252 excepción 0.06258893013 haskey 0.0973930358887 –

+0

cómo 'defaultdict' se compara aquí? – Kuzeko

Cuestiones relacionadas