In line with fail-fast philosophy, dict access with d[k] raises an error if k is not an existing key. A common solution is d.get(k, default) which return a default rather handling KeyError if k is not present.
This post introduces three alternatives - setdefault, defaultdict and __mising for this situation.
setdefault
Link to heading
word = 'helloapplepineworld'
index = {}
for i, ch in enumerate(word):
index.setdefault(ch, []).append(i)
# {'h': [0], 'e': [1, 9, 13], 'l': [2, 3, 8, 17], 'o': [4, 15], 'a': [5], 'p': [6, 7, 10], 'i': [11], 'n': [12], 'w': [14], 'r':[16], 'd': [18]}
print(index)
index.setdefault(ch, []).append(i) is the same as running
if ch not in index:
index[ch] = []
index[ch].append(i)
except the later code performs at least two searches for ch - three if not found, which setdefault does it all with a single lookup.
defaultdict
Link to heading
Sometimes it is convenient to have mappings that return some made-up value when a missing key is searched. defaultdict is created for doing this.
When instantiating a defaultdict you provide a callable used to produce a default value whenever __getitem__ is passed a non-existent key argument.
For example, given an empty defaultdict created as d = defaultdict(list), if k is not in d then the expression d[k] does the following steps:
- calls
list()to create a new list - inserts the list into
dusingkas key - returns a reference to that list
from collections import defaultdict
word = 'helloapplepineworld'
index = defaultdict(list)
for i, ch in enumerate(word):
index[ch].append[i]
Implementing __missing__
Link to heading
__missing__ method is not defined in the base dict class, but if you subclass dict and provide a __missing__ method, the standard dict.__getitem__ will call it whenever a key is not found instead of rasing a KeyError.
class StrKeyDict(dict):
def __missing__(self, key):
if isinstance(key, str):
raise KeyError(key)
return self[str(key)]
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
def __contains__(self, key):
return key in self.keys() or str(key) in self.keys()
d = StrKeyDict([('2','two'),('4','four')])
print(d['2'], d[4]) # two four
Reference