11

Working with deeply nested python dicts, I would like to be able to assign values in such a data structure like this:

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

without having to check that mydict[key] etc. are actually set to be a dict, e.g. using

  if not key in mydict: mydict[key]={}

The creation of subdictionaries should happen on the fly. What is the most elegant way to allow something equivalent - maybe using decorators on the standard <type 'dict'>?

martineau
  • 99,260
  • 22
  • 139
  • 249
relet
  • 6,204
  • 1
  • 30
  • 41
  • Related question: http://stackoverflow.com/questions/3122566/in-a-python-dict-of-dicts-how-do-you-emulate-perls-auto-vivification-behavior/3122575#3122575 – unutbu Aug 04 '10 at 11:52

3 Answers3

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

d = D()
d['a']['b']['c'] = 3
John La Rooy
  • 263,347
  • 47
  • 334
  • 476
  • 1
    You have to be careful with this. `d['a'] = 2 d['a']['b'] = 2` will fail. – unholysampler Aug 04 '10 at 11:50
  • Yes, but that is implied in my question - I am asking for a mixed type of dicts and values. – relet Aug 04 '10 at 11:54
  • To solve the case d['a'] = 2 d['a']['b'] = 2, I would even write class D(dict): def __missing__(self, key): value = self[key] = type(self)() return value def __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 Feb 13 '16 at 04:34
  • 1
    See [this answer](http://stackoverflow.com/a/19829714/355230) for a slightly improved implementation of this in `class Vividict`. – martineau Dec 07 '16 at 02:04
13

You could use a tuple as the key for the dict and then you don't have to worry about subdictionaries at all:

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

Alternatively, if you really need to have subdictionaries for some reason you could use collections.defaultdict.

For two levels this is straightforward:

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

For three it's slightly more complex:

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

Four and more levels are left as an exercise for the reader. :-)

Dave Webb
  • 179,733
  • 56
  • 298
  • 296
3

I like Dave's answer better, but here's an alternative.

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

It's definitely not pretty to have to use lambdas to implements the inner defaulted collections, but apparently necessary.

I82Much
  • 25,421
  • 13
  • 84
  • 117
  • 3
    Lambdas are never necessary: you can always use a named function instead. In this case using a named function would at least mean the `repr` has something a bit more meaningful than `lambda`. – Duncan Aug 04 '10 at 14:12