Is there a standard class for an infinitely nested defaultdict?

瘦欲@ 提交于 2019-11-28 08:33:12

You can derive from defaultdict to get the behavior you want:

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)

Usage of this class would look like this:

>>> d = Counters()
>>> d[1][2][3].total = 5
>>> d[1][2][3].show()
0 out of 5
>>> d[5].show()
0 out of 0

This lends itself naturally to a recursive definition.

>>> 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
michaelkrisper

I think this one-liner is a nearly perfect solution:

>>> from collections import defaultdict
>>> infinite_defaultdict = lambda: defaultdict(infinite_defaultdict)
>>> d = infinite_defaultdict() 
>>> d['x']['y']['z'] = 10

by Raymond Hettinger on Twitter (https://twitter.com/raymondh/status/343823801278140417)

The ideal solution, inspired by sth's answer:

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

In case after eight years you are still thinking about how to get this with a one-liner:

from collections import defaultdict

t = defaultdict(lambda: defaultdict(t.default_factory))

This is close:

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

It's not quite what you want, since the most deeply nested level does not have your default 0 values for 'count' or 'total'.

Edited: Ah, this works now - just needed to add a __getattr__ method, and this does what you want.

Edit 2: Now you can define other factory methods for the attributes, besides ints. But they all have to be the same type, can't have count be float and total be int.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!