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
appleis not infruit_basket, it will raiseKeyErrorexception 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_basketisTrueorFalseif '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
getmethod.
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
defaultdictinstead.
TLDR
- There are four methods to detect and handle missing dictionary keys:
inKeyErrorgetsetdefault
getmethod is best for dictionaries with basic types and also with dictionary values of a high cost- Use
defaultdictinstead ofsetdefault