¿Cuál es la mejor manera de inicializar un dict de dicts en Python?
autovivification (5)
Esta pregunta ya tiene una respuesta aquí:
Muchas veces en Perl, haré algo como esto:
$myhash{foo}{bar}{baz} = 1
¿Cómo traduciría 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 mejor manera?
¿Hay alguna razón por la que deba ser un dict de dicts? Si no hay una razón convincente para esa estructura en particular, puedes simplemente indexar el 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 inicializa el dict con una clave de tupla, pero puede omitirlos al establecer / obtener valores usando []:
mydict = {} # Initialized the dict
mydict[''foo'', ''bar'', ''baz''] = 1 # Sets a value
mydict[''foo'', ''bar'', ''baz''] # Returns 1
Los diccionarios anidados como ese se llaman (a menudo) objetos de un pobre hombre. Sí, hay una implicación y podría correlacionarse con la naturaleza orientada a objetos pitones.
Si la cantidad de anidamiento que necesita es fija, collections.defaultdict
es maravilloso.
por ejemplo, anidar dos profundidades:
myhash = collections.defaultdict(dict)
myhash[1][2] = 3
myhash[1][3] = 13
myhash[2][4] = 9
Si quieres ir a otro nivel de anidación, deberás 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
editar: MizardX señala que podemos obtener una genérica completa 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
Supongo que la traducción literal sería:
mydict = {''foo'' : { ''bar'' : { ''baz'':1}}}
Vocación:
>>> mydict[''foo''][''bar''][''baz'']
te da 1.
Sin embargo, eso me parece un poco asqueroso.
(No soy un tipo perl, así que supongo que lo que hace tu perl)
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}}}