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
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
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
d
usingk
as 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__
__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