¿Hay una clase estándar para un defaultdict nested infinitamente?

¿Alguien sabe si hay una clase estándar para un diccionario que se puede anidar infinitamente en Python?

Me estoy repitiendo este patrón:

d = defaultdict(lambda: defaultdict(lambda: defaultdict(int))) d['abc']['def']['xyz'] += 1 

Si quiero agregar “otra capa” (por ejemplo, d['abc']['def']['xyz']['wrt'] ), tengo que definir otro anidamiento de defaultdicts.

Para generalizar este patrón, escribí una clase simple que anula __getitem__ para crear automáticamente el siguiente diccionario nested.

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 Google, pero no estoy seguro de cómo se llamaría esto.

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 

Esto se presta naturalmente a una definición recursiva.

 >>> import collections >>> def nested_dd(): ... return collections.defaultdict(nested_dd) ... >>> foo = nested_dd() >>> foo defaultdict(, {}) >>> foo[1][2]=3 >>> foo[1] defaultdict(, {2: 3}) >>> foo[1][2] 3 

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 )

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 

En el caso de que después de ocho años aún esté pensando en cómo obtener esto con una sola línea:

 from collections import defaultdict t = defaultdict(lambda: defaultdict(t.default_factory)) 

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 desea, ya que el nivel más profundamente nested no tiene sus valores predeterminados de 0 para ‘contar’ o ‘total’.

Editado: Ah, esto funciona ahora, solo necesitaba agregar un método __getattr__ , y esto hace lo que quieres.

Edit 2: Ahora puede definir otros métodos de fábrica para los atributos, además de ints. Pero todos tienen que ser del mismo tipo, no se puede tener en cuenta la flotación y la totalidad debe ser int.