我有一个数据结构,本质上相当于一个嵌套的字典。假设它是这样的:

{'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()。


当前回答

如果嵌套层的数量很少,我使用collections.defaultdict:

from collections import defaultdict

def nested_dict_factory(): 
  return defaultdict(int)
def nested_dict_factory2(): 
  return defaultdict(nested_dict_factory)
db = defaultdict(nested_dict_factory2)

db['new jersey']['mercer county']['plumbers'] = 3
db['new jersey']['mercer county']['programmers'] = 81

像这样使用defaultdict可以避免大量混乱的setdefault()、get()等。

其他回答

我发现setdefault非常有用;它检查一个键是否存在,如果不存在就添加它:

d = {}
d.setdefault('new jersey', {}).setdefault('mercer county', {})['plumbers'] = 3

Setdefault总是返回相关的键,所以你实际上是在原地更新'd'的值。

说到迭代,我相信你可以很容易地编写一个生成器,如果Python中还没有这样的生成器:

def iterateStates(d):
    # Let's count up the total number of "plumbers" / "dentists" / etc.
    # across all counties and states
    job_totals = {}

    # I guess this is the annoying nested stuff you were talking about?
    for (state, counties) in d.iteritems():
        for (county, jobs) in counties.iteritems():
            for (job, num) in jobs.iteritems():
                # If job isn't already in job_totals, default it to zero
                job_totals[job] = job_totals.get(job, 0) + num

    # Now return an iterator of (job, number) tuples
    return job_totals.iteritems()

# Display all jobs
for (job, num) in iterateStates(d):
    print "There are %d %s in total" % (job, num)

如果嵌套层的数量很少,我使用collections.defaultdict:

from collections import defaultdict

def nested_dict_factory(): 
  return defaultdict(int)
def nested_dict_factory2(): 
  return defaultdict(nested_dict_factory)
db = defaultdict(nested_dict_factory2)

db['new jersey']['mercer county']['plumbers'] = 3
db['new jersey']['mercer county']['programmers'] = 81

像这样使用defaultdict可以避免大量混乱的setdefault()、get()等。

至于“讨厌的尝试/捕捉障碍”:

d = {}
d.setdefault('key',{}).setdefault('inner key',{})['inner inner key'] = 'value'
print d

收益率

{'key': {'inner key': {'inner inner key': 'value'}}}

你可以使用它从你的平面字典格式转换为结构化格式:

fd = {('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}

for (k1,k2,k3), v in fd.iteritems():
    d.setdefault(k1, {}).setdefault(k2, {})[k3] = v

你可以使用Addict: https://github.com/mewwts/addict

>>> from addict import Dict
>>> my_new_shiny_dict = Dict()
>>> my_new_shiny_dict.a.b.c.d.e = 2
>>> my_new_shiny_dict
{'a': {'b': {'c': {'d': {'e': 2}}}}}

除非您的数据集将保持相当小,否则您可能会考虑使用关系数据库。它将完全满足您的需要:方便添加计数、选择计数子集,甚至按州、县、职业或这些的任何组合进行汇总计数。