¿Cómo puedo acceder a un diccionario profundamente anidado utilizando tuplas?
Me gustaría ampliarel ejemplo de autovivificación dado en una respuesta anterior deNosklo para permitir el acceso al diccionario por tupla.
La solución de nosklo se ve así:
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}}}
Tengo un caso en el que quiero establecer un nodo dada una tupla arbitraria de subíndices. Si no sé cuántas capas de profundidad tendrá la tupla, ¿cómo puedo diseñar una manera de establecer el nodo apropiado?
Estoy pensando que quizás podría usar una sintaxis como la siguiente:
mytuple = (1,2,3)
a[mytuple] = 4
Pero estoy teniendo problemas para encontrar una implementación que funcione.
ActualizarTengo un ejemplo completamente funcional basado en la respuesta de @ JCash:
class NestedDict(dict):
"""
Nested dictionary of arbitrary depth with autovivification.
Allows data access via extended slice notation.
"""
def __getitem__(self, keys):
# Let's assume *keys* is a list or tuple.
if not isinstance(keys, basestring):
try:
node = self
for key in keys:
node = dict.__getitem__(node, key)
return node
except TypeError:
# *keys* is not a list or tuple.
pass
try:
return dict.__getitem__(self, keys)
except KeyError:
raise KeyError(keys)
def __setitem__(self, keys, value):
# Let's assume *keys* is a list or tuple.
if not isinstance(keys, basestring):
try:
node = self
for key in keys[:-1]:
try:
node = dict.__getitem__(node, key)
except KeyError:
node[key] = type(self)()
node = node[key]
return dict.__setitem__(node, keys[-1], value)
except TypeError:
# *keys* is not a list or tuple.
pass
dict.__setitem__(self, keys, value)
Lo que puede lograr el mismo resultado que arriba utilizando la notación de sector extendida:
d = NestedDict()
d[1,2,3] = 4
d[1,3,3] = 5
d[1,2,'test'] = 6