实现嵌套字典的最佳方法是什么?
- 2024-12-03 08:44:00
- admin 原创
- 158
问题描述:
我有一个数据结构,它本质上相当于一个嵌套字典。假设它看起来像这样:
{'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()
手动设置。
解决方案 1:
在 Python 中实现嵌套字典的最佳方法是什么?
这是个坏主意,不要这么做。相反,使用普通字典并使用dict.setdefault
where 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
。(返回值分配很有用,因为它避免了我们在字典上额外调用 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
使用演示
下面只是一个例子,说明如何轻松使用此字典动态创建嵌套字典结构。这可以快速创建您想要的深度的分层树结构。
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)
但是如果您需要检查数据,则以相同方式填充数据的自动生动的默认字典的结果如下所示:
>>> 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
其他解决方案,并且在每种需要这种行为的情况下我都会这样做。
解决方案 2:
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}}}
解决方案 3:
只是因为我还没有见过这么小的字典,这里有一个可以根据需要嵌套的字典,毫不费力:
# yo dawg, i heard you liked dicts
def yodict():
return defaultdict(yodict)
解决方案 4:
您可以创建一个 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(...)
。
解决方案 5:
由于您有一个星型模式设计,您可能希望将其结构设计得更像关系表而不是字典。
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 开销大有帮助。
解决方案 6:
如果嵌套级别数较少,我会使用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()
等等。
解决方案 7:
这是一个返回任意深度的嵌套字典的函数:
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 递归转换为普通dict
s。
def dictify(d):
for k,v in d.iteritems():
if isinstance(v,defaultdict):
d[k] = dictify(v)
return dict(d)
解决方案 8:
正如其他人所建议的那样,关系数据库对您来说可能更有用。您可以使用内存中的 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"'))
这只是一个简单的例子。您可以为州、县和职位定义单独的表。
解决方案 9:
我发现它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)
解决方案 10:
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)
解决方案 11:
您可以使用 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}}}}}
解决方案 12:
至于“令人讨厌的 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
解决方案 13:
defaultdict()
是你的朋友!
对于二维字典,您可以执行以下操作:
d = defaultdict(defaultdict)
d[1][2] = 3
如需更多维度,您可以:
d = defaultdict(lambda :defaultdict(defaultdict))
d[1][2][3] = 4
解决方案 14:
为了轻松迭代嵌套字典,为什么不编写一个简单的生成器呢?
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']
我在这里对您的数据结构的含义做出了假设,但应该很容易根据您实际想要做的事情进行调整。
解决方案 15:
我喜欢将其包装在一个类中并实现的想法__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
解决方案 16:
除非您的数据集非常小,否则您可能需要考虑使用关系数据库。它将完全满足您的需求:轻松添加计数、选择计数子集,甚至按州、县、职业或这些的任意组合汇总计数。
解决方案 17:
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
)查询时返回字典,否则返回单个值。
解决方案 18:
我也有类似的事情。我有很多这样的情况:
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')
好些了吗?我想是的。
解决方案 19:
您可以在 lambda 和 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})})})
解决方案 20:
我曾经使用过这个功能。它安全、快速、易于维护。
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
>>>
解决方案 21:
对于以下内容(从上面复制),有没有办法实现附加函数。我正在尝试使用嵌套字典将值存储为数组。
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'
解决方案 22:
我可以把这一切都放到一个类中,但似乎有人可能已经这样做了。
NestedDict
开源软件包(我是作者)中的课程试图ndicts
减轻处理嵌套字典的痛苦。我认为它满足了问题要求的所有要求。
这里是其功能的摘要,更多详细信息请查看文档。
初始化
>>> 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]