【问题标题】:Is there a standard class for an infinitely nested defaultdict?是否有无限嵌套的默认字典的标准类?
【发布时间】:2011-05-09 20:37:58
【问题描述】:

有谁知道 Python 中是否有用于无限嵌套字典的标准类?

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

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

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

为了概括这种模式,我编写了一个简单的类,它覆盖 __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

但是,有人知道这个想法的预先存在的实现吗?我试过谷歌搜索,但我不确定这会叫什么。

【问题讨论】:

    标签: python dictionary nested infinite defaultdict


    【解决方案1】:

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

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

    雷蒙德·海廷格在推特上 (https://twitter.com/raymondh/status/343823801278140417)

    【讨论】:

    • 这个解决方案应该排名更高!
    • 优雅如普拉达
    【解决方案2】:

    这自然适用于递归定义。

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

    【讨论】:

    • 非常整洁,但与我发布的答案有相同的缺陷,叶节点不支持“计数”和“总”零值。
    • @Paul -- 确实;你不能把蛋糕也吃掉!对counttotal 的特殊支持会破坏对称性,因此必须将其硬连线到某个类中。
    • @aaronasterling:任何递归限制都没有问题,因为该函数不会递归调用自身。
    • 对,我错过了那部分并继续寻找 :) 抱歉 katriealex。
    • 请原谅我,但我认为这行不通,因为如果您看到 OP 的问题,他会使用 defaultdict 的默认值进行大量计算,而不仅仅是赋值!!!跨度>
    【解决方案3】:

    您可以从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
    

    【讨论】:

    • 谢谢。这最接近我所寻找的,并帮助我找到了确切解决方案。
    【解决方案4】:

    理想的解决方案,灵感来自某某的回答:

    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】:

      如果八年后您仍然在考虑如何用单线做到这一点:

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

      【讨论】:

        【解决方案6】:

        这很接近:

        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。

        【讨论】:

        • 小建议:可以使用functools.partial(type(self), attrFactory)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-10
        • 1970-01-01
        • 2016-01-30
        • 2015-06-02
        • 1970-01-01
        • 2011-06-29
        相关资源
        最近更新 更多