Handling missing dict keys

• 2 min read

Trying to access a non-existent key using this notation: dict[key] raises a KeyError. An easy workaround for this is to use get(key) instead, which returns None if key isn't found, or get(key, default) which returns default if key isn't found.

>>> d = {}
>>> d
{}
>>> d["x"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'x'
>>> d.get("x")
>>> d
{}
>>> d.get("x","y")
'y'
>>> d
{}
>>>

But what if you want to not only return a default if the key is missing, but also assign that default to the specified key in the dict? There are a couple of ways to do that.

1. setdefault

setdefault(key[, default])

If key is in the dictionary, return its value. If not, insert key with a value of default and return default. default defaults to None.

docs

>>> d
{}
>>> d.setdefault("x","y")
'y'
>>> d
{'x': 'y'}
>>>

2. defaultdict

class collections.defaultdict(default_factory=None, /[, ...])

The first argument provides the initial value for the default factory attribute; it defaults to None. All remaining arguments are treated the same as if they were passed to the dict constructor, including keyword arguments.

docs

>>> from collections import defaultdict
>>> i = defaultdict(int)
>>> i
defaultdict(<class 'int'>, {})
>>> i["x"]
0
>>> i
defaultdict(<class 'int'>, {'x': 0})
>>> i["y"]
0
>>> i
defaultdict(<class 'int'>, {'x': 0, 'y': 0})
>>>

Note that no KeyError is raised despite the key not existing at first. Instead, the key is created with the default value of the type passed into defaultdict, in this case 0 for int. If we used list instead, the default value would be [], an so on.

Further reading:

3. Implement __missing__

This is actually what defaultdict does behind the scenes. Use this when defaultdict doesn't fit your usecase.

>>> class M(dict):
...     def __missing__(self, key):
...             value = "my default value"
...             self[key] = value
...             return value
...
>>>
>>> m = M()
>>> m
{}
>>> m["x"]
'my default value'
>>> m
{'x': 'my default value'}
>>> m["y"]
'my default value'
>>> m
{'x': 'my default value', 'y': 'my default value'}
>>>
🏷  python