2009-03-16 18 views
66

Muchas veces en Perl, voy a hacer algo como esto:¿Cuál es la mejor manera de inicializar un dict de dicts en Python?

$myhash{foo}{bar}{baz} = 1 

¿Cómo puedo traducir esto a Python? Hasta ahora tengo:

if not 'foo' in myhash: 
    myhash['foo'] = {} 
if not 'bar' in myhash['foo']: 
    myhash['foo']['bar'] = {} 
myhash['foo']['bar']['baz'] = 1 

¿Hay una manera mejor?

+0

ejem, de hecho, el otro se le preguntó 5 días antes y estos dos tienen 4 años ... –

Respuesta

83
class AutoVivification(dict): 
    """Implementation of perl's autovivification feature.""" 
    def __getitem__(self, item): 
     try: 
      return dict.__getitem__(self, item) 
     except KeyError: 
      value = self[item] = type(self)() 
      return value 

Pruebas:

a = AutoVivification() 

a[1][2][3] = 4 
a[1][3][3] = 5 
a[1][2]['test'] = 6 

print a 

Salida:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}} 
+3

¿Es posible ampliarlo para que admita el siguiente comportamiento: a [1] [2] [3] + = some_value. Entonces, si la clave no existía de antemano, entonces a a [1] [2] [3] se inicializaría con el valor predeterminado del tipo (some_value)? –

+4

Esta función tiene el efecto secundario de que cualquier intento de obtener una clave inexistente también crea la clave. Por lo general, solo desea crear automáticamente una clave si, al mismo tiempo, estaba configurando una tecla o subclave. –

+0

¿Hay también una manera de hacer que la asignación sea variable? Entonces, dado que 'var = [1,2,3]', podría hacer como 'a [var] = 1', que se expandiría a' a [1] [2] [3] = 1'? – PascalVKooten

2

supongo que la traducción literal sería:

mydict = {'foo' : { 'bar' : { 'baz':1}}} 

Calling:

>>> mydict['foo']['bar']['baz'] 

le da 1.

que se parece un poco bruto para mí, sin embargo.

(No soy un chico Perl, sin embargo, así que supongo que en lo que hace su Perl)

+1

Eso solo funciona en el momento de la inicialización, ¿verdad? – mike

+0

No estoy seguro de lo que quieres decir. – Dana

+0

@Dana, en lugar de agregar nuevos valores a mydict durante el tiempo de ejecución. –

2

diccionarios anidados como que son (a menudo) llamaron a un pobre objetos Mans. Sí, hay una implicación y podría correlacionarse con la naturaleza orientada a objetos pitones.

12

¿Existe una razón por la que tiene que ser un diccionario de dicts? Si no hay ninguna razón de peso para que la estructura particular, usted podría simplemente índice de la dict con una tupla:

mydict = {('foo', 'bar', 'baz'):1} # Initializes dict with a key/value pair 
mydict[('foo', 'bar', 'baz')]  # Returns 1 

mydict[('foo', 'unbar')] = 2  # Sets a value for a new key 

Los paréntesis son necesarios si inicializar el dict con una llave tupla, pero se puede omitir cuando configure/conseguir valores utilizando []:

mydict = {}      # Initialized the dict 
mydict['foo', 'bar', 'baz'] = 1 # Sets a value 
mydict['foo', 'bar', 'baz']  # Returns 1 
+0

¿Puede tener claro cuándo puede omitir los paréntesis? ¿Es porque la coma es el operador de tupla, y los paréntesis solo son necesarios si tenemos una agrupación ambigua? – Kiv

+0

Aclaración adicional, thx. – zweiterlinde

+0

Esto en realidad puede ser más rápido que los diccionarios anidados porque hay una búsqueda en lugar de tres. – ChaimG

86

Si se fija la cantidad de anidación que necesita, collections.defaultdict es maravilloso.

p. Ej. anidar dos de profundidad:

myhash = collections.defaultdict(dict) 
myhash[1][2] = 3 
myhash[1][3] = 13 
myhash[2][4] = 9 

Si quieres ir a otro nivel de anidamiento, tendrá que hacer algo como:

myhash = collections.defaultdict(lambda : collections.defaultdict(dict)) 
myhash[1][2][3] = 4 
myhash[1][3][3] = 5 
myhash[1][2]['test'] = 6 

edición: MizardX señala que podemos conseguir plena generalidad con una función simple:

import collections 
def makehash(): 
    return collections.defaultdict(makehash) 

Ahora podemos hacer:

myhash = makehash() 
myhash[1][2] = 4 
myhash[1][3] = 8 
myhash[2][5][8] = 17 
# etc 
+4

o def makehash(): devuelve collections.defaultdict (makehash); myhash = makehash() –

+0

No tengo problemas con las funciones recursivas "tradicionales", pero hay algo sobre eso que me parece poco intuitivo. Impar. ¡Gracias de todos modos! –

+1

Gracias por esto. Ese lambda: defaultdict() es lo que necesitaba. – wheaties

Cuestiones relacionadas