Autovivication and Y Combinator in Python

Sometimes you may need to have nested dictionaries initialized without much hassle.

“Autovivication” comes in handy. What you want to achieve is:

>>> d = dict()
>>> d['q']['w']['r'] = 777

Of course what you’ll get will be:

>>> d['q']['w']['r'] = 777
Traceback (most recent call last):
  File “<stdin>”, line 1, in <module>
KeyError: ‘q’

If you don’t need deeply nested dictionaries defaultdict with dict default factory will do the job:

>>> from collections import defaultdict
>>> d = defaultdict(dict)
>>> d
defaultdict(<type ‘dict’>, {})
>>> d['a']['b'] = 33
>>> d
defaultdict(<type ‘dict’>, {’a': {’b': 33}})
>>> d['a']['b']
33

Here are another ways to have unlimited level of nested dictionaries.
1) Define custom class inheriting from built-in dict:

class default_dict(dict):
    def __missing__(self, key):
        self[key] = value = self.__class__()
        return value
>>> d = default_dict()
>>> d
{}
>>> d['q']['w']['r'] = 777
>>> d
{’q': {’w': {’r': 777}}}
>>> d['q']['w']['r']
777

2) Recursive function + defaultdict:

def make_dict():
    return defaultdict(make_dict)
>>> from collections import defaultdict
>>> d = defaultdict(make_dict)
>>> d
defaultdict(<function make_dict at 0xb7c5295c>, {})
>>> d['q']['w']['r'] = 777
>>> d
defaultdict(<function make_dict at 0xb7c5295c>, {’q': defaultdict(<function make_dict at 0xb7c5295c>, {’w': defaultdict(<function make_dict at 0xb7c5295c>, {’r': 777})})})
>>> d['q']['w']['r']
777

3) And for fun you can use Y Combinator (in short - making anonymous lambda recursive) which is actually as (2) but you don’t have to have recursive function defined:

def Y(g):
    return ((lambda f: f(f))
            (lambda f:
                 g(lambda x: f(f)
                   (x))))

defdict = Y(lambda mk_dict:
                lambda x=None: defaultdict(lambda x=None: mk_dict(x)))
>>> d = defdict()
>>> d
defaultdict(<function <lambda> at 0xb7c52a74>, {})
>>> d['q']['w']['r'] = 777
>>> d
defaultdict(<function <lambda> at 0xb7c52a74>, {’q': defaultdict(<function <lambda> at 0xb7c56c6c>, {’w': defaultdict(<function <lambda> at 0xb7c56a74>, {’r': 777})})})
>>> d['q']['w']['r']
777

4 Responses to “Autovivication and Y Combinator in Python”

  1. Maxime Biais Says:

    Thanks for the tip, I often write code with defaultdict(dict) but i never had to use a deeper dict structure.

  2. Ur Says:

    already seen here:
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/537636

  3. Ruslan Spivak Says:

    Yep, though here I showed additional ways to implement autovivication.

    Here is from the same author with my comment:
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/537637

  4. Ur Says:

    Yes, You right.
    But maybe its possible to modify this to access without square brackets in attribute style. Instead d['q']['w']['r'] = 777 => d.q.w.r = 777 ???
    already seen that this is possible, but sorry, I don’t remember where…

Leave a Reply