A dictionary of horrors

This post demonstrates a strange behaviour encountered while initializing a dictionary using the dict.fromkeys method. TLDR: be careful while passing mutable arguments such as lists.

In [1]:
planets = ("Mercury", "Venus", "Earth", "Mars")

sattelites = dict.fromkeys(planets, value=[])
sattelites
Out[1]:
{'Mercury': [], 'Venus': [], 'Earth': [], 'Mars': []}
In [2]:
sattelites["Earth"].append("Moon")

What you expect

>>> sattelites
{'Mercury': [], 'Venus': [], 'Earth': ['Moon'], 'Mars': []}

What you actually get

In [3]:
sattelites
Out[3]:
{'Mercury': ['Moon'], 'Venus': ['Moon'], 'Earth': ['Moon'], 'Mars': ['Moon']}

Why?

Surely string as keys are valid and hashable, no doubt about that, but this behaviour is weird.

In [4]:
id(sattelites["Earth"]), id(sattelites["Mars"])
Out[4]:
(139764445576200, 139764445576200)

Apparently the same list instance is assigned to all the dictionary items which gets mutated. This is also the case if you initialize as follows.

In [5]:
sattelites = dict.fromkeys(planets, list())
id(sattelites["Earth"]), id(sattelites["Mars"])
Out[5]:
(139764445511368, 139764445511368)

The id is still the same across dictionaries!

The solution: Use dictionary comprehensions

In [6]:
sattelites = {planet: [] for planet in planets}
sattelites
Out[6]:
{'Mercury': [], 'Venus': [], 'Earth': [], 'Mars': []}
In [7]:
sattelites["Earth"].append("Moon")
sattelites
Out[7]:
{'Mercury': [], 'Venus': [], 'Earth': ['Moon'], 'Mars': []}
In [8]:
id(sattelites["Earth"]), id(sattelites["Mars"])
Out[8]:
(139764445567048, 139764351351752)

Finally the ids are different :)

You can download this notebook, or see a static view on nbviewer.

My research centers around geophysical flows, particularly with gravity waves and vortices, studied from a turbulence perspective. I am trained in computational, theoretical and experimental tools. You would find me using things which are simple, efficent, open-source, and reproducible. Feel free to contact me - I am usually reachable via email or the social accounts listed below.