I feel that I still don’t know Python that well and proper Pythonic way of doing things. One of the things I learned from a senior member of my team was to use get
instead of checking KeyError
by directly accessing a dictionary’s attribute. Now I’ve got more used to it and I remember to use get
.
Example
fruit_basket = {
"apple": 2,
"banana": 5
}
When I want to find out how many apple is there, I can do the followings:
- Accessing apple’s count by doing
fruit_basket['apple']
- In this case, if
apple
is not infruit_basket
, it will raiseKeyError
exception when we try to access the value.
try: count = fruit_basket['apple'] except KeyError: count = 0
- Only need to access once and assign once.
- In this case, if
- Checking
'apple' in fruit_basket
isTrue
orFalse
if 'apple' in fruit_basket: count = fruit_basket['apple'] else: count = 0
This flow of fetching a key that exists or returning a default value is so common and Python’s built-in dict
provides get()
method (ex: fruit_basket.get('apple')
) and it is often better than the above two.
Python dict
’s get
method
Syntax
key = 'apple'
fruit_basket.get(key, 0)
The second parameter 0
is the default value to return in case the key is not present. This also requires only one access and one assignment.
Reason
Using get
is much shorter than KeyError
or in
examples which suffer from code duplication for assignments. For a dictionary with simple types, using get
method is the shortest and clearest option.
Python dictionary with more complex type
Using in
with more complex type
votes = {
'americano': ['Paul', 'Mary'],
'cafe latte': ['Steve', 'Casey'],
}
key = 'cappuccino'
if key in votes:
names = votes[key]
else:
votes[key] = names = []
- Requires two access if the key is present.
- Requires one access and one assignment if the key is missing.
Using KeyError
exception for more complex type
try:
names = votes[key]
except KeyError:
votes[key] = names = []
- Requires one access if the key is present.
- Requires one access and one assignment if the key is missing.
Using get
method for more complex type
if (names := votes.get(key)) is None:
votes[key] = names = []
:=
is assignment expression
Using setdefault
method of dict
The setdefault
method tries to fetch the value of a key in the dictionary and if the key is not present, the method assigns the key to the default value provided. This can implement the same logic as in get
method.
names = votes.setdefault(key, [])
- This is shorter than using
get
method.
Downsides with using setdefault
- However, the readability may not be good (‘set default’ while getting the value)
- The default value is assigned directly into the dictionary if the key is missing instead of being copied
data = {} key = 'foo' value = [] data.setdefault(key, value) print('Before:', data) value.append('hello') print('After:', data) >>> Before: {'foo': []} After: {'foo': ['hello']}
- This may mean that we’ll need to construct a new default value for each key, and this can be a significant performance overhead. If we re-use the object for the default value, it may lead to strange behaviour or bugs.
Use cases for setdefault
- When the default values are cheap to construct, mutable, and there’s no potential for raising exceptions
- Except these special cases, we may want to consider using
defaultdict
instead.
TLDR
- There are four methods to detect and handle missing dictionary keys:
in
KeyError
get
setdefault
get
method is best for dictionaries with basic types and also with dictionary values of a high cost- Use
defaultdict
instead ofsetdefault