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

我有一个很大的清单,比如:

[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

我知道如果我使用递归缺省值,我可以编写 table[A][B1][C1]=1table[A][B2]=2,但这只有在我硬编码那些插入语句时才有效。

在解析列表时,我不知道在调用 table[key1][key2][...]之前需要多少个[]。

43592 次浏览

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

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将其作为嵌套字典的结构来读取,您可以执行类似的操作。

我会用定义 __missing__dict子类来实现:

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

您不能直接使用默认值,因为 预期工厂函数在初始化时,但是在初始化时,没有办法描述相同的默认值。上面的结构做的事情与默认 dict 相同,但是由于它是一个命名类(NestedDict) ,它可以在遇到丢失的键时引用自己。也可以子类化 default 并覆盖 __init__

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

from collections import defaultdict


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


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

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

# Define recursive dictionary
from collections import defaultdict
tree = lambda: defaultdict(tree)

用法:

# Create instance
mydict = tree()


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


# Print
import prettyprint
prettyprint.pp(mydict)

产出:

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

这与上面的相同,但是避免使用 lambda 符号。也许更容易阅读?

def dict_factory():
return defaultdict(dict_factory)


your_dict = dict_factory()

另外——从注释中——如果您希望从现有的 dict 进行更新,可以简单地调用

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

为了向 dict 添加值。

一个稍微不同的可能性,允许常规的字典初始化:

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

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: {}}}}}}

To add To@Hugo < br > To have a max deep:

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

您可以使用递归 defaultdict来实现这一点。

from collections import defaultdict


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

在闭包(“ private”本地函数作用域)中保护默认工厂名称 the_tree非常重要。避免使用一行程序 lambda版本,这个版本由于 Python 的 延迟装订封闭而被窃听,而是使用 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

你可以用 NestedDict

from ndicts.ndicts import NestedDict


nd = NestedDict()
nd[0, 1, 2, 3, 4, 5] = 6

作为一本字典,结果是:

>>> nd.to_dict()
{0: {1: {2: {3: {4: {5: 6}}}}}}

安装 指控

pip install ndicts