具有可变深度的多级默认值?

时间:2011-03-20 16:20:01

标签: python dictionary

我有一个很大的列表,如:

[A][B1][C1]=1
[A][B1][C2]=2
[A][B2]=3
[D][E][F][G]=4

我想构建一个多级别的词典,如:

A
--B1
-----C1=1
-----C2=1
--B2=3
D
--E
----F
------G=4

我知道如果我使用递归defaultdict,我可以编写table[A][B1][C1]=1table[A][B2]=2,但这只有在我对这些insert语句进行硬编码时才有效。

在解析列表时,我没有事先需要多少[]来调用table[key1][key2][...]

10 个答案:

答案 0 :(得分:133)

你甚至不用定义一个类就可以做到:

from collections import defaultdict

nested_dict = lambda: defaultdict(nested_dict)
nest = nested_dict()

nest[0][1][2][3][4][5] = 6

答案 1 :(得分:16)

您的示例说,在任何级别都可以有值,也可以是子元素的字典。这称为,并且有许多可用的实现。这是一个:

from collections import defaultdict
class Tree(defaultdict):
    def __init__(self, value=None):
        super(Tree, self).__init__(Tree)
        self.value = value

root = Tree()
root.value = 1
root['a']['b'].value = 3
print root.value
print root['a']['b'].value
print root['c']['d']['f'].value

输出:

1
3
None

您可以通过在JSON中编写输入并使用json.load将其作为嵌套字典的结构来读取来执行类似操作。

答案 2 :(得分:9)

我会使用dict的子类来定义__missing__

>>> class NestedDict(dict):
...     def __missing__(self, key):
...             self[key] = NestedDict()
...             return self[key]
...
>>> table = NestedDict()
>>> table['A']['B1']['C1'] = 1
>>> table
{'A': {'B1': {'C1': 1}}}

你无法直接使用defaultdict,因为defaultdict expects the factory function在初始化时,但在初始化时,没有办法描述相同的defaultdict。上面的构造与默认dict的作用相同,但由于它是一个命名类(NestedDict),它可以引用自身,因为遇到了丢失的键。也可以将defaultdict子类化并覆盖__init__

答案 3 :(得分:6)

我认为递归字典的最简单实现是这样的。只有叶节点可以包含值。

# Define recursive dictionary
tree = lambda: defaultdict(tree)

用法:

# Create instance
mydict = tree()

tree['a'] = 1
tree['b']['a'] = 2
tree['c']
tree['d']['a']['b'] = 0

# Print
import prettyprint
prettyprint.pp(tree)

输出:

{
  "a": 1, 
  "b": {
    "a": 1
  }, 
  "c": {},
  "d": {
    "a": {
      "b": 0
    }
  }
}

答案 4 :(得分:4)

这相当于上面的内容,但避免使用lambda表示法。也许更容易阅读?

def dict_factory():
   return defaultdict(dict_factory)

your_dict = dict_factory()

另外 - 从评论中 - 如果你想从现有的词典更新,你可以直接打电话

your_dict[0][1][2].update({"some_key":"some_value"})

为了向字典添加值。

答案 5 :(得分:3)

Dan O' Huiginn在2010年的期刊上发布了一个非常好的解决方案:

http://ohuiginn.net/mt/2010/07/nested_dictionaries_in_python.html

>>> class NestedDict(dict):
...     def __getitem__(self, key):
...         if key in self: return self.get(key)
...         return self.setdefault(key, NestedDict())


>>> eggs = NestedDict()
>>> eggs[1][2][3][4][5]
{}
>>> eggs
{1: {2: {3: {4: {5: {}}}}}}

答案 6 :(得分:2)

允许常规字典初始化的可能性略有不同:

from collections import defaultdict

def superdict(arg=()):
    update = lambda obj, arg: obj.update(arg) or obj
    return update(defaultdict(superdict), arg)

示例:

>>> d = {"a":1}
>>> sd = superdict(d)
>>> sd["b"]["c"] = 2

答案 7 :(得分:1)

添加到@Hugo
要获得最大深度:

l=lambda x:defaultdict(lambda:l(x-1)) if x>0 else defaultdict(dict)
arr = l(2)

答案 8 :(得分:0)

您可以通过递归defaultdict来实现。

from collections import defaultdict

def tree():
    def the_tree():
        return defaultdict(the_tree)
    return the_tree()

在闭包(“私有”本地函数作用域)中保护默认工厂名称the_tree很重要。避免使用单行lambda版本,该版本由于Python的late binding closures而被错误诊断,而应使用def来实现。

使用lambda接受的答案存在一个缺陷,即实例必须依赖外部作用域中存在的nested_dict名称。如果出于某种原因无法解析工厂名称(例如,工厂名称被重新绑定或删除),则先前存在的实例也将被巧妙地破坏:

>>> nested_dict = lambda: defaultdict(nested_dict)
>>> nest = nested_dict()
>>> nest[0][1][2][3][4][6] = 7
>>> del nested_dict
>>> nest[8][9] = 10
# NameError: name 'nested_dict' is not defined

答案 9 :(得分:-4)

拥有table['A']=defaultdict()