programiz - Diccionario infinitamente anidado en Python
python dictionary of dictionaries (6)
Esto está cerca:
class recursivedefaultdict(defaultdict):
def __init__(self, attrFactory=int):
self.default_factory = lambda : type(self)(attrFactory)
self._attrFactory = attrFactory
def __getattr__(self, attr):
newval = self._attrFactory()
setattr(self, attr, newval)
return newval
d = recursivedefaultdict(float)
d[''abc''][''def''][''xyz''].count += 0.24
d[''abc''][''def''][''xyz''].total += 1
data = [
(''A'',''B'',''Z'',1),
(''A'',''C'',''Y'',2),
(''A'',''C'',''X'',3),
(''B'',''A'',''W'',4),
(''B'',''B'',''V'',5),
(''B'',''B'',''U'',6),
(''B'',''D'',''T'',7),
]
table = recursivedefaultdict(int)
for k1,k2,k3,v in data:
table[k1][k2][k3] = v
No es exactamente lo que quiere, ya que el nivel más anidado no tiene sus valores 0 predeterminados para ''contar'' o ''total''.
Editado: Ah, esto funciona ahora - solo necesitaba agregar un método __getattr__
, y esto hace lo que quiere.
Editar 2: ahora puede definir otros métodos de fábrica para los atributos, además de los enteros. Pero todos tienen que ser del mismo tipo, no pueden tener el conteo ser flotante y el total ser int.
¿Alguien sabe si hay una clase estándar para un diccionario infinitamente encajable en Python?
Me encuentro repitiendo este patrón:
d = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
d[''abc''][''def''][''xyz''] += 1
Si deseo agregar "otra capa" (por ejemplo, d[''abc''][''def''][''xyz''][''wrt'']
), tengo que definir otra anidación de los predeterminados.
Para generalizar este patrón, he escrito una clase simple que anula __getitem__
para crear automáticamente el siguiente diccionario anidado.
p.ej
d = InfiniteDict((''count'',0),(''total'',0))
d[''abc''][''def''][''xyz''].count += 0.24
d[''abc''][''def''][''xyz''].total += 1
d[''abc''][''def''][''xyz''][''wrt''].count += 0.143
d[''abc''][''def''][''xyz''][''wrt''].total += 1
Sin embargo, ¿alguien sabe de una implementación preexistente de esta idea? He intentado con Google, pero no estoy seguro de cómo se llamaría.
Esto se presta naturalmente a una definición recursiva.
>>> import collections
>>> def nested_dd():
... return collections.defaultdict(nested_dd)
...
>>> foo = nested_dd()
>>> foo
defaultdict(<function nested_dd at 0x023F0E30>, {})
>>> foo[1][2]=3
>>> foo[1]
defaultdict(<function nested_dd at 0x023F0E30>, {2: 3})
>>> foo[1][2]
3
La solución ideal, inspirada en la respuesta de sth:
from collections import defaultdict
class InfiniteDict(defaultdict):
def __init__(self, **kargs):
defaultdict.__init__(self, lambda: self.__class__(**kargs))
self.__dict__.update(kargs)
d = InfiniteDict(count=0, total=0)
d[''abc''][''def''].count += 0.25
d[''abc''][''def''].total += 1
print d[''abc''][''def''].count
print d[''abc''][''def''].total
d[''abc''][''def''][''xyz''].count += 0.789
d[''abc''][''def''][''xyz''].total += 1
print d[''abc''][''def''][''xyz''].count
print d[''abc''][''def''][''xyz''].total
Puede derivar de defaultdict
para obtener el comportamiento que desea:
class InfiniteDict(defaultdict):
def __init__(self):
defaultdict.__init__(self, self.__class__)
class Counters(InfiniteDict):
def __init__(self):
InfiniteDict.__init__(self)
self.count = 0
self.total = 0
def show(self):
print "%i out of %i" % (self.count, self.total)
El uso de esta clase se vería así:
>>> d = Counters()
>>> d[1][2][3].total = 5
>>> d[1][2][3].show()
0 out of 5
>>> d[5].show()
0 out of 0
Simplemente se llama "un árbol". Hay un módulo que parece hacer lo que quieres.
Creo que este one-liner es una solución casi perfecta:
>>> from collections import defaultdict
>>> infinite_defaultdict = lambda: defaultdict(infinite_defaultdict)
>>> d = infinite_defaultdict()
>>> d[''x''][''y''][''z''] = 10
por Raymond Hettinger en Twitter ( https://twitter.com/raymondh/status/343823801278140417 )