我有一个数据结构,本质上相当于一个嵌套的字典。假设它是这样的:
{'new jersey': {'mercer county': {'plumbers': 3,
'programmers': 81},
'middlesex county': {'programmers': 81,
'salesmen': 62}},
'new york': {'queens county': {'plumbers': 9,
'salesmen': 36}}}
现在,维护和创造这个是相当痛苦的;每次我有一个新的州/县/职业,我都必须通过讨厌的try/catch块创建下层字典。此外,如果我想遍历所有值,就必须创建恼人的嵌套迭代器。
我也可以使用元组作为键,像这样:
{('new jersey', 'mercer county', 'plumbers'): 3,
('new jersey', 'mercer county', 'programmers'): 81,
('new jersey', 'middlesex county', 'programmers'): 81,
('new jersey', 'middlesex county', 'salesmen'): 62,
('new york', 'queens county', 'plumbers'): 9,
('new york', 'queens county', 'salesmen'): 36}
这使得遍历值非常简单和自然,但在语法上更痛苦的事情,如聚合和查看字典的子集(例如,如果我只想逐个状态查看)。
基本上,有时我想把一个嵌套字典看作一个平面字典,有时我想把它看作一个复杂的层次结构。我可以把这些都打包到一个类中,但似乎有人已经这样做了。或者,似乎有一些非常优雅的语法结构可以做到这一点。
我怎样才能做得更好呢?
附录:我知道setdefault(),但它并不是真正的干净语法。此外,您创建的每个子字典仍然需要手动设置setdefault()。
这是一个返回任意深度的嵌套字典的函数:
from collections import defaultdict
def make_dict():
return defaultdict(make_dict)
像这样使用它:
d=defaultdict(make_dict)
d["food"]["meat"]="beef"
d["food"]["veggie"]="corn"
d["food"]["sweets"]="ice cream"
d["animal"]["pet"]["dog"]="collie"
d["animal"]["pet"]["cat"]="tabby"
d["animal"]["farm animal"]="chicken"
迭代所有内容,如下所示:
def iter_all(d,depth=1):
for k,v in d.iteritems():
print "-"*depth,k
if type(v) is defaultdict:
iter_all(v,depth+1)
else:
print "-"*(depth+1),v
iter_all(d)
打印出来:
- food
-- sweets
--- ice cream
-- meat
--- beef
-- veggie
--- corn
- animal
-- pet
--- dog
---- labrador
--- cat
---- tabby
-- farm animal
--- chicken
你可能最终想要这样做,使新的项目不能添加到字典。递归地将所有这些defaultdict转换为普通dict很容易。
def dictify(d):
for k,v in d.iteritems():
if isinstance(v,defaultdict):
d[k] = dictify(v)
return dict(d)
我可以把这些都打包到一个类中,但似乎有人已经这样做了。
来自开源ndicts包(我是作者)的NestedDict类试图减轻处理嵌套字典的痛苦。我认为它满足了所有问题的要求。
这里有它的功能概要,要了解更多细节,请查看文档。
初始化
>>> from ndicts import NestedDict
>>> nd = NestedDict({"a": {"aa": 0}, "b": 1})
得到项目
把NestedDict看作是一个扁平的字典。
>>> nd["a", "aa"]
0
同时,您可以获得中间节点,而不仅仅是叶值。
>>> nd["a"]
{"aa": 0}
如果键不存在,则抛出异常。
>>> nd["asd"]
Traceback (most recent call last):
...
KeyError: ('asd',)
设置项
与普通字典一样,如果缺少一个键,则将它添加到NestedDict中。
>>> nd["a", "ab"] = 2
>>> nd
NestedDict({"a": {"aa": 0, "ab": 2}, "b": 1})
这允许从一个空的NestedDict开始,可以通过设置新项来激活它。
迭代
谈到迭代,可以把NestedDict看作是一个扁平的字典。我们熟悉的.keys(), .values()和.item()方法是可用的。
>>> [key for key in nd]
[('a', 'aa'), ('a', 'ab'), ('b',)]
>>> [value for value in nd.values()]
[0, 2, 1]