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

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


当前回答

对于下面(从上面复制),是否有一种方法来实现append函数。我试图使用嵌套字典存储值作为数组。

class Vividict(dict):
    def __missing__(self, key):
        value = self[key] = type(self)() # retain local pointer to value
    return value  

我目前的实现如下:

totalGeneHash=Vividict()
        
for keys in GenHash:
    for second in GenHash[keys]:
        if keys in sampleHash:
            total_val = GenHash[keys][second]
                totalGeneHash[gene][keys].append(total_val)
This is the error I get: AttributeError: 'Vividict' object has no attribute 'append'

其他回答

我以前用过这个函数。安全、快捷、易于维护。

def deep_get(dictionary, keys, default=None):
    return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)

例子:

>>> from functools import reduce
>>> def deep_get(dictionary, keys, default=None):
...     return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)
...
>>> person = {'person':{'name':{'first':'John'}}}
>>> print (deep_get(person, "person.name.first"))
John
>>> print (deep_get(person, "person.name.lastname"))
None
>>> print (deep_get(person, "person.name.lastname", default="No lastname"))
No lastname
>>>

由于您有一个星型模式设计,您可能希望它的结构更像一个关系表,而不是字典。

import collections

class Jobs( object ):
    def __init__( self, state, county, title, count ):
        self.state= state
        self.count= county
        self.title= title
        self.count= count

facts = [
    Jobs( 'new jersey', 'mercer county', 'plumbers', 3 ),
    ...

def groupBy( facts, name ):
    total= collections.defaultdict( int )
    for f in facts:
        key= getattr( f, name )
        total[key] += f.count

这类事情对于创建一个没有SQL开销的类似数据仓库的设计大有帮助。

我喜欢把它包装在一个类中,并实现__getitem__和__setitem__,这样它们就实现了一个简单的查询语言:

>>> d['new jersey/mercer county/plumbers'] = 3
>>> d['new jersey/mercer county/programmers'] = 81
>>> d['new jersey/mercer county/programmers']
81
>>> d['new jersey/mercer country']
<view which implicitly adds 'new jersey/mercer county' to queries/mutations>

如果你想要更花哨,你也可以实现如下内容:

>>> d['*/*/programmers']
<view which would contain 'programmers' entries>

但大多数情况下,我认为这样的事情执行起来真的很有趣: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]

您可以创建一个YAML文件,并使用PyYaml读取它。

第一步:创建一个YAML文件"employment.yml":

new jersey:
  mercer county:
    pumbers: 3
    programmers: 81
  middlesex county:
    salesmen: 62
    programmers: 81
new york:
  queens county:
    plumbers: 9
    salesmen: 36

第二步:用Python阅读

import yaml
file_handle = open("employment.yml")
my_shnazzy_dictionary = yaml.safe_load(file_handle)
file_handle.close()

现在my_shnazzy_dictionary有你所有的值。如果需要动态执行此操作,可以将YAML创建为字符串,并将其提供给YAML .safe_load(…)。