动态生成字典键

12

在处理深度嵌套的Python字典时,我希望能够像这样对这样的数据结构进行赋值:

  mydict[key][subkey][subkey2]="value"

不需要检查mydict[key]是否设置为字典,例如使用

  if not key in mydict: mydict[key]={}
创建子字典应该是动态的。有什么优雅的方法可以实现类似的功能吗?也许可以在标准 <type 'dict'> 上使用装饰器?

相关问题:https://dev59.com/SnA75IYBdhLWcg3wv7_A#3122575 - unutbu
3个回答

28
class D(dict):
    def __missing__(self, key):
        self[key] = D()
        return self[key]

d = D()
d['a']['b']['c'] = 3

1
你必须小心处理这个问题。d['a'] = 2 d['a']['b'] = 2 这段代码会失败。 - unholysampler
是的,但这已经包含在我的问题中了 - 我正在请求混合类型的字典和值。 - relet
为了解决 d['a'] = 2 d['a']['b'] = 2 的情况,我甚至会编写类D(dict): def missing(self, key): value = self[key] = type(self)() return valuedef __getitem__(self, key): values = dict.__getitem__(self, key) if isinstance(values, dict): values = SafeDict(values) if isinstance(values, list): for i, v in enumerate(values): if isinstance(v, dict): values[i] = SafeDict(v) return values - user2346922
1
请参考此答案,其中class Vividict的实现略有改进。 - martineau

14

你可以使用元组作为字典的键,这样你就不必担心子字典了:

mydict[(key,subkey,subkey2)] = "value"

如果出于某种原因您确实需要使用子字典,可以考虑使用collections.defaultdict

对于两个级别的情况,这很简单:

>>> from collections import defaultdict
>>> d = defaultdict(dict)
>>> d['key']['subkey'] = 'value'
>>> d['key']['subkey']
'value'

对于3个元素,稍微复杂一些:

>>> d = defaultdict(lambda: defaultdict(dict))
>>> d['key']['subkey']['subkey2'] = 'value'
>>> d['key']['subkey']['subkey2']
'value'

四个及以上的级别留给读者作为练习。 :-)


太棒了。我很高兴问了,因为我不明白我怎么会错过这个。 :) - relet
好答案。你能提供使用 defaultdict 的方法吗?嵌套一次很容易:mydict = defaultdict(dict)。但是有没有更优雅的解决方案可以嵌套两次? - Johannes Charra
@jellybean - 刚刚添加了三级解决方案;还不错。 - David Webb
1
嗯,元组解决方案确实破坏了对子字典进行迭代的能力,不是吗? - relet
@relet - 这就是为什么我也提供了defaultdict解决方案。 - David Webb

3
我更喜欢Dave的答案,但这里有一个替代方案。
from collections import defaultdict
d = defaultdict(lambda : defaultdict(int))
>>> d['a']['b'] += 1
>>> d
defaultdict(<function <lambda> at 0x652f0>, {'a': defaultdict(<type 'int'>, {'b': 1})})
>>> d['a']['b']
1

http://tumble.philadams.net/post/85269428/python-nested-defaultdicts

在实现内部默认集合时使用lambda函数确实不太美观,但显然是必要的。


3
Lambda表达式并不是必需的:您可以始终使用具名函数替代它。在这种情况下,使用具名函数至少意味着reprlambda更有意义些。 - Duncan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接