Published on

Python dictionary like Clojure map

Authors
  • avatar
    Name
    Piotr Kurnik
    Twitter

Imagine you want to create something like this:


expected = {"Continent": {"Europe": {"Poland": 38,
                                     "Germany": 82}}}


In Clojure you can use assoc-in to produce such data structure. It’s very useful because we start with empty map {}

(-> {}
    (assoc-in [:Continent :Europe :Poland] 38)
    (assoc-in [:Continent :Europe :Germany] 82)
    )

If you try to do something similar in Python you will get a KeyError

data = dict()
data["Continent"]["Europe"]["Poland"] = 38

You can use collections.defaultdict to create a dictionary that has the same properties as Clojure maps:

from collections import defaultdict

def default_dict_factory():
    return defaultdict(default_dict_factory)

data = default_dict_factory()

data["Continent"]["Europe"]["Poland"] = 38
data["Continent"]["Europe"]["Germany"] = 38

print(data)

That works!

How does that work?

defaultdict accepts factory method, which is executed when the key is not found in the dictionary. And that is what default_dict_factory returns.

In theory you could do:

data = defaultdict(dict)

That would allow you do run something like that:

data["Continent"] = "Europe"

But as soon as you want to run:

data["Continent"]["Europe"]

You will get KeyError. Why?

Because in this case factory method is dict which means that when key is not found, a dict is created. And dict raises KeyError.

If you use default_dict_factory, on missing key defaultdict will be created, which is what you want.

Using deafultdict is a simple way of creating Python dictionaries which work like Clojure maps. I use them quite frequently, hope you will find it useful too.