As mentioned an issue with dct.get(key, default) is that, if key exists but is mapped to a falsy value (e.g. None), you will get that value, not default.
If you want to return a default value even when key exists but is mapped to None, you can use this implementation:
class MyDict(dict):
def myget(self, key, default=None):
"""Get value by key, return default if key is missing or is mapped to a falsy value."""
val = self.get(key, default)
if val is None:
val = default
if isinstance(val, dict):
val = MyDict(val)
return val
Let's see an use case.
Suppose you get external data from a third-party that looks like this:
d1 = {"name":"Mark", "belongings":{"bycicles":2, "cars":5, "trucks":2}}
d2 = {"name":"Peter"}
d3 = {"name":"John", "belongings":None}
To get the number of cars that belongs to Mark, you would do:
d1.get("belongings").get("cars") # 5
and this would correctly return 5.
But if you use this to get the number of Peter's cars:
d2.get("belongings").get("cars") # throws AttributeError: 'NoneType' object has no attribute 'get'
you would get an Exception because d2 does not have a belongings key, so the first gest returns None.
To fix this, you would do:
d2.get("belongings", {}).get("cars", 0) # returns 0
This owuld correctly returns 0.
Now let's get the number of John's cars:
d3.get("belongings", {}).get("cars", 0) # throws AttributeError: 'NoneType' object has no attribute 'get'
This would throw an error because in d3 the key belongings is mapped to a None value, which is then returned.
Now let's use our implementation:
MyDict(d3).myget("belongings", {}).myget("cars", 0) # 0
.get(key)instead of[key]