217

我有一个基本上相当于嵌套字典的数据结构。假设它看起来像这样:

{'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()手动设置。

4

21 回答 21

198

在 Python 中实现嵌套字典的最佳方法是什么?

这是一个坏主意,不要这样做。相反,请使用常规字典并使用dict.setdefaultwhere apropos,因此当在正常使用下缺少键时,您会得到预期的KeyError. 如果您坚持要采取这种行为,请按以下步骤射击自己:

__missing__在子类上实现dict以设置并返回一个新实例。

这种方法自 Python 2.5 起就可用(并记录在案) ,并且(对我特别有价值)它像普通的 dict 一样漂亮地打印出来,而不是自动生成 defaultdict 的丑陋打印:

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

(注意self[key]在赋值的左边,所以这里没有递归。)

并说你有一些数据:

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

这是我们的使用代码:

vividict = Vividict()
for (state, county, occupation), number in data.items():
    vividict[state][county][occupation] = number

现在:

>>> import pprint
>>> pprint.pprint(vividict, width=40)
{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36}}}

批评

对这种容器的批评是,如果用户拼错了一个键,我们的代码可能会默默地失败:

>>> vividict['new york']['queens counyt']
{}

此外,现在我们的数据中有一个拼写错误的县:

>>> pprint.pprint(vividict, width=40)
{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36},
              'queens counyt': {}}}

解释:

Vividict每当一个键被访问但丢失时,我们只是提供我们类的另一个嵌套实例。(返回赋值是有用的,因为它避免了我们额外调用 dict 上的 getter,不幸的是,我们不能在设置时返回它。)

请注意,这些语义与最受好评的答案相同,但只有一半的代码行 - nosklo 的实现:

class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

使用示范

下面只是一个示例,说明如何轻松使用此 dict 动态创建嵌套的 dict 结构。这可以快速创建一个层次结构的树结构,就像你想去的一样深。

import pprint

class Vividict(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

d = Vividict()

d['foo']['bar']
d['foo']['baz']
d['fizz']['buzz']
d['primary']['secondary']['tertiary']['quaternary']
pprint.pprint(d)

哪个输出:

{'fizz': {'buzz': {}},
 'foo': {'bar': {}, 'baz': {}},
 'primary': {'secondary': {'tertiary': {'quaternary': {}}}}}

正如最后一行所示,它打印得很漂亮,可以进行人工检查。但是,如果您想直观地检查您的数据,实施__missing__将其类的新实例设置为键并返回它是一个更好的解决方案。

其他替代方案,用于对比:

dict.setdefault

虽然提问者认为这不干净,但我觉得它比Vividict我自己更可取。

d = {} # or dict()
for (state, county, occupation), number in data.items():
    d.setdefault(state, {}).setdefault(county, {})[occupation] = number

现在:

>>> pprint.pprint(d, width=40)
{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36}}}

拼写错误会大声失败,并且不会使我们的数据因错误信息而混乱:

>>> d['new york']['queens counyt']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'queens counyt'

此外,我认为 setdefault 在循环中使用时效果很好,并且您不知道您将获得什么键,但是重复使用变得非常繁琐,我认为没有人会想要跟上以下内容:

d = dict()

d.setdefault('foo', {}).setdefault('bar', {})
d.setdefault('foo', {}).setdefault('baz', {})
d.setdefault('fizz', {}).setdefault('buzz', {})
d.setdefault('primary', {}).setdefault('secondary', {}).setdefault('tertiary', {}).setdefault('quaternary', {})

另一个批评是 setdefault 无论是否使用都需要一个新实例。然而,Python(或至少 CPython)在处理未使用和未引用的新实例方面相当聪明,例如,它重用内存中的位置:

>>> id({}), id({}), id({})
(523575344, 523575344, 523575344)

一个自动激活的默认字典

这是一个看起来很整洁的实现,并且在您没有检查数据的脚本中使用将与实现一样有用__missing__

from collections import defaultdict

def vivdict():
    return defaultdict(vivdict)

但是,如果您需要检查您的数据,以相同方式填充数据的自动激活 defaultdict 的结果如下所示:

>>> d = vivdict(); d['foo']['bar']; d['foo']['baz']; d['fizz']['buzz']; d['primary']['secondary']['tertiary']['quaternary']; import pprint; 
>>> pprint.pprint(d)
defaultdict(<function vivdict at 0x17B01870>, {'foo': defaultdict(<function vivdict 
at 0x17B01870>, {'baz': defaultdict(<function vivdict at 0x17B01870>, {}), 'bar': 
defaultdict(<function vivdict at 0x17B01870>, {})}), 'primary': defaultdict(<function 
vivdict at 0x17B01870>, {'secondary': defaultdict(<function vivdict at 0x17B01870>, 
{'tertiary': defaultdict(<function vivdict at 0x17B01870>, {'quaternary': defaultdict(
<function vivdict at 0x17B01870>, {})})})}), 'fizz': defaultdict(<function vivdict at 
0x17B01870>, {'buzz': defaultdict(<function vivdict at 0x17B01870>, {})})})

这个输出很不雅,结果很不可读。通常给出的解决方案是递归地转换回字典以进行手动检查。这个重要的解决方案留给读者作为练习。

表现

最后,让我们看看性能。我正在减去实例化的成本。

>>> import timeit
>>> min(timeit.repeat(lambda: {}.setdefault('foo', {}))) - min(timeit.repeat(lambda: {}))
0.13612580299377441
>>> min(timeit.repeat(lambda: vivdict()['foo'])) - min(timeit.repeat(lambda: vivdict()))
0.2936999797821045
>>> min(timeit.repeat(lambda: Vividict()['foo'])) - min(timeit.repeat(lambda: Vividict()))
0.5354437828063965
>>> min(timeit.repeat(lambda: AutoVivification()['foo'])) - min(timeit.repeat(lambda: AutoVivification()))
2.138362169265747

基于性能,dict.setdefault效果最好。如果您关心执行速度,我强烈推荐它用于生产代码。

如果您需要将其用于交互式使用(也许在 IPython 笔记本中),那么性能并不重要 - 在这种情况下,我会使用 Vividict 来提高输出的可读性。与 AutoVivification 对象(使用__getitem__而不是__missing__为此目的而制作的 )相比,它要优越得多。

结论

__missing__在子类上实现dict以设置和返回新实例比替代方案稍微困难一些,但具有以下好处

  • 简单的实例化
  • 简单的数据填充
  • 轻松查看数据

并且由于它比修改更简单且性能更高__getitem__,因此应该首选该方法。

尽管如此,它也有缺点:

  • 错误的查找会默默地失败。
  • 错误的查找将保留在字典中。

因此,我个人更喜欢setdefault其他解决方案,并且在我需要这种行为的每一种情况下都有。

于 2013-11-07T06:53:24.557 回答
189
class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

测试:

a = AutoVivification()

a[1][2][3] = 4
a[1][3][3] = 5
a[1][2]['test'] = 6

print a

输出:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}}
于 2009-03-16T21:53:36.700 回答
33

只是因为我还没有见过这么小的一个,这里有一个你喜欢嵌套的字典,没有汗水:

# yo dawg, i heard you liked dicts                                                                      
def yodict():
    return defaultdict(yodict)
于 2011-09-19T19:51:07.967 回答
26

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

第 1 步:创建 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

第 2 步:用 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(...).

于 2009-03-11T20:08:17.090 回答
19

由于您有星型模式设计,因此您可能希望将其构建得更像关系表而不是字典。

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 开销。

于 2009-03-11T17:29:12.700 回答
16

如果嵌套级别的数量很少,我会使用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()等。

于 2009-03-11T20:02:11.053 回答
11

这是一个返回任意深度的嵌套字典的函数:

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递归地将所有这些s 转换为普通s很容易dict

def dictify(d):
    for k,v in d.iteritems():
        if isinstance(v,defaultdict):
            d[k] = dictify(v)
    return dict(d)
于 2014-03-29T22:53:36.880 回答
7

我觉得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)
于 2009-03-11T17:14:19.960 回答
7

正如其他人所建议的那样,关系数据库可能对您更有用。您可以使用内存中的 sqlite3 数据库作为数据结构来创建表,然后查询它们。

import sqlite3

c = sqlite3.Connection(':memory:')
c.execute('CREATE TABLE jobs (state, county, title, count)')

c.executemany('insert into jobs values (?, ?, ?, ?)', [
    ('New Jersey', 'Mercer County',    'Programmers', 81),
    ('New Jersey', 'Mercer County',    'Plumbers',     3),
    ('New Jersey', 'Middlesex County', 'Programmers', 81),
    ('New Jersey', 'Middlesex County', 'Salesmen',    62),
    ('New York',   'Queens County',    'Salesmen',    36),
    ('New York',   'Queens County',    'Plumbers',     9),
])

# some example queries
print list(c.execute('SELECT * FROM jobs WHERE county = "Queens County"'))
print list(c.execute('SELECT SUM(count) FROM jobs WHERE title = "Programmers"'))

这只是一个简单的例子。您可以为州、县和职位定义单独的表格。

于 2009-03-13T20:24:47.303 回答
5

collections.defaultdict可以进行子分类以制作嵌套字典。然后将任何有用的迭代方法添加到该类。

>>> from collections import defaultdict
>>> class nesteddict(defaultdict):
    def __init__(self):
        defaultdict.__init__(self, nesteddict)
    def walk(self):
        for key, value in self.iteritems():
            if isinstance(value, nesteddict):
                for tup in value.walk():
                    yield (key,) + tup
            else:
                yield key, value


>>> nd = nesteddict()
>>> nd['new jersey']['mercer county']['plumbers'] = 3
>>> nd['new jersey']['mercer county']['programmers'] = 81
>>> nd['new jersey']['middlesex county']['programmers'] = 81
>>> nd['new jersey']['middlesex county']['salesmen'] = 62
>>> nd['new york']['queens county']['plumbers'] = 9
>>> nd['new york']['queens county']['salesmen'] = 36
>>> for tup in nd.walk():
    print tup


('new jersey', 'mercer county', 'programmers', 81)
('new jersey', 'mercer county', 'plumbers', 3)
('new jersey', 'middlesex county', 'programmers', 81)
('new jersey', 'middlesex county', 'salesmen', 62)
('new york', 'queens county', 'salesmen', 36)
('new york', 'queens county', 'plumbers', 9)
于 2009-03-12T06:27:52.437 回答
4

至于“令人讨厌的 try/catch 块”:

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
于 2009-03-11T17:17:40.263 回答
4

defaultdict()是你的朋友!

对于二维字典,您可以执行以下操作:

d = defaultdict(defaultdict)
d[1][2] = 3

对于更多维度,您可以:

d = defaultdict(lambda :defaultdict(defaultdict))
d[1][2][3] = 4
于 2011-03-07T16:48:34.257 回答
4

您可以使用 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}}}}}
于 2016-01-21T18:50:09.343 回答
3

为了轻松迭代嵌套字典,为什么不写一个简单的生成器呢?

def each_job(my_dict):
    for state, a in my_dict.items():
        for county, b in a.items():
            for job, value in b.items():
                yield {
                    'state'  : state,
                    'county' : county,
                    'job'    : job,
                    'value'  : value
                }

那么,如果你有你的复杂嵌套字典,迭代它就变得很简单:

for r in each_job(my_dict):
    print "There are %d %s in %s, %s" % (r['value'], r['job'], r['county'], r['state'])

显然,您的生成器可以生成对您有用的任何格式的数据。

为什么要使用 try catch 块来读取树?在尝试检索密钥之前查询字典中是否存在密钥很容易(并且可能更安全)。使用保护子句的函数可能如下所示:

if not my_dict.has_key('new jersey'):
    return False

nj_dict = my_dict['new jersey']
...

或者,一种可能有点冗长的方法是使用 get 方法:

value = my_dict.get('new jersey', {}).get('middlesex county', {}).get('salesmen', 0)

但是对于一种更简洁的方式,您可能希望使用collections.defaultdict,它是自 python 2.5 以来标准库的一部分。

import collections

def state_struct(): return collections.defaultdict(county_struct)
def county_struct(): return collections.defaultdict(job_struct)
def job_struct(): return 0

my_dict = collections.defaultdict(state_struct)

print my_dict['new jersey']['middlesex county']['salesmen']

我在这里对您的数据结构的含义做出假设,但是应该很容易根据您实际想要做的事情进行调整。

于 2009-03-11T20:05:32.137 回答
2

我喜欢将其包装在一个类中并实现的想法,__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

于 2009-03-11T17:19:27.467 回答
1
class JobDb(object):
    def __init__(self):
        self.data = []
        self.all = set()
        self.free = []
        self.index1 = {}
        self.index2 = {}
        self.index3 = {}

    def _indices(self,(key1,key2,key3)):
        indices = self.all.copy()
        wild = False
        for index,key in ((self.index1,key1),(self.index2,key2),
                                             (self.index3,key3)):
            if key is not None:
                indices &= index.setdefault(key,set())
            else:
                wild = True
        return indices, wild

    def __getitem__(self,key):
        indices, wild = self._indices(key)
        if wild:
            return dict(self.data[i] for i in indices)
        else:
            values = [self.data[i][-1] for i in indices]
            if values:
                return values[0]

    def __setitem__(self,key,value):
        indices, wild = self._indices(key)
        if indices:
            for i in indices:
                self.data[i] = key,value
        elif wild:
            raise KeyError(k)
        else:
            if self.free:
                index = self.free.pop(0)
                self.data[index] = key,value
            else:
                index = len(self.data)
                self.data.append((key,value))
                self.all.add(index)
            self.index1.setdefault(key[0],set()).add(index)
            self.index2.setdefault(key[1],set()).add(index)
            self.index3.setdefault(key[2],set()).add(index)

    def __delitem__(self,key):
        indices,wild = self._indices(key)
        if not indices:
            raise KeyError
        self.index1[key[0]] -= indices
        self.index2[key[1]] -= indices
        self.index3[key[2]] -= indices
        self.all -= indices
        for i in indices:
            self.data[i] = None
        self.free.extend(indices)

    def __len__(self):
        return len(self.all)

    def __iter__(self):
        for key,value in self.data:
            yield key

例子:

>>> db = JobDb()
>>> db['new jersey', 'mercer county', 'plumbers'] = 3
>>> db['new jersey', 'mercer county', 'programmers'] = 81
>>> db['new jersey', 'middlesex county', 'programmers'] = 81
>>> db['new jersey', 'middlesex county', 'salesmen'] = 62
>>> db['new york', 'queens county', 'plumbers'] = 9
>>> db['new york', 'queens county', 'salesmen'] = 36

>>> db['new york', None, None]
{('new york', 'queens county', 'plumbers'): 9,
 ('new york', 'queens county', 'salesmen'): 36}

>>> db[None, None, 'plumbers']
{('new jersey', 'mercer county', 'plumbers'): 3,
 ('new york', 'queens county', 'plumbers'): 9}

>>> db['new jersey', 'mercer county', None]
{('new jersey', 'mercer county', 'plumbers'): 3,
 ('new jersey', 'mercer county', 'programmers'): 81}

>>> db['new jersey', 'middlesex county', 'programmers']
81

>>>

编辑:现在使用通配符 ( None) 查询时返回字典,否则返回单个值。

于 2009-03-11T18:52:03.843 回答
1

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

于 2009-03-11T20:30:20.800 回答
0

我也有类似的事情。我有很多我这样做的案例:

thedict = {}
for item in ('foo', 'bar', 'baz'):
  mydict = thedict.get(item, {})
  mydict = get_value_for(item)
  thedict[item] = mydict

但是要深入很多层次。“.get(item, {})” 是关键,因为如果还没有字典,它将创建另一个字典。与此同时,我一直在想办法更好地处理这个问题。现在有很多

value = mydict.get('foo', {}).get('bar', {}).get('baz', 0)

所以相反,我做了:

def dictgetter(thedict, default, *args):
  totalargs = len(args)
  for i,arg in enumerate(args):
    if i+1 == totalargs:
      thedict = thedict.get(arg, default)
    else:
      thedict = thedict.get(arg, {})
  return thedict

如果你这样做,效果是一样的:

value = dictgetter(mydict, 0, 'foo', 'bar', 'baz')

更好的?我认同。

于 2012-10-19T18:47:35.937 回答
0

您可以在 lambdas 和 defaultdict 中使用递归,无需定义名称:

a = defaultdict((lambda f: f(f))(lambda g: lambda:defaultdict(g(g))))

这是一个例子:

>>> a['new jersey']['mercer county']['plumbers']=3
>>> a['new jersey']['middlesex county']['programmers']=81
>>> a['new jersey']['mercer county']['programmers']=81
>>> a['new jersey']['middlesex county']['salesmen']=62
>>> a
defaultdict(<function __main__.<lambda>>,
        {'new jersey': defaultdict(<function __main__.<lambda>>,
                     {'mercer county': defaultdict(<function __main__.<lambda>>,
                                  {'plumbers': 3, 'programmers': 81}),
                      'middlesex county': defaultdict(<function __main__.<lambda>>,
                                  {'programmers': 81, 'salesmen': 62})})})
于 2016-04-09T04:25:21.250 回答
0

我曾经使用过这个功能。它安全、快速、易于维护。

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
>>>
于 2017-10-23T14:27:01.710 回答
0

对于以下(从上面复制)有一种方法可以实现附加功能。我正在尝试使用嵌套字典将值存储为数组。

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'
于 2020-09-16T00:06:39.067 回答