代码之家  ›  专栏  ›  技术社区  ›  Cerin

有无限嵌套的defaultdict的标准类吗?

  •  11
  • Cerin  · 技术社区  · 14 年前

    有人知道Python中是否有无限嵌套字典的标准类吗?

    我发现自己在重复这个模式:

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

    如果我想添加“另一层”(例如。 d['abc']['def']['xyz']['wrt'] ),我必须定义另一个defaultdict嵌套。

    为了推广这种模式,我编写了一个简单的类来重写 __getitem__ 自动创建下一个嵌套字典。

    例如

    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
    

    然而,有人知道这个想法的预先存在的实施吗?我试过用谷歌搜索,但我不知道这叫什么。

    6 回复  |  直到 6 年前
        1
  •  11
  •   sth    14 年前

    你可以从 defaultdict 要获得所需的行为:

    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)
    

    这个类的用法如下:

    >>> d = Counters()
    >>> d[1][2][3].total = 5
    >>> d[1][2][3].show()
    0 out of 5
    >>> d[5].show()
    0 out of 0
    
        2
  •  14
  •   Katriel    14 年前

    这自然会产生递归定义。

    >>> 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
    
        3
  •  9
  •   Quentin Pradet    8 年前

    我认为这一行是一个近乎完美的解决方案:

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

    作者Raymond Hettinger在Twitter上( https://twitter.com/raymondh/status/343823801278140417 )

        4
  •  3
  •   Cerin    14 年前

    从某物的回答中得到启发的理想解决方案:

    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
    
        5
  •  1
  •   Tommaso Barbugli    6 年前

    以防八年后,你还在考虑如何用一个单程:

    from collections import defaultdict
    
    t = defaultdict(lambda: defaultdict(t.default_factory))
    
        6
  •  0
  •   PaulMcG    14 年前

    很接近:

    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
    

    这不是您想要的,因为最深嵌套的级别没有“count”或“total”的默认0值。

    编辑: 啊,现在可以了-只需要添加一个 __getattr__ 方法,这就是你想要的。

    编辑2: 现在,除了int之外,您还可以为属性定义其他工厂方法。但它们都必须是同一类型,不能有count是float,total是int。