You can use get twice:
example_dict.get('key1', {}).get('key2')
This will return None if key1 or key2 does not exist.
Note that this can still raise an AttributeError if example_dict['key1'] exists but is not a dict (or a dictated object with a get method). You sent try..except code you sent instead of TypeError if example_dict['key1'] not a subscription.
Another difference is that the try...except short circuit is immediately after the first missing key. The get call chain does not work.
If you want to keep the syntax example_dict['key1']['key2'] , but don't want it to ever raise KeyErrors, you can use the Hasher recipe :
class Hasher(dict): # /questions/35890/generating-dictionary-keys-on-the-fly/261439#261439 def __missing__(self, key): value = self[key] = type(self)() return value example_dict = Hasher() print(example_dict['key1']) # {} print(example_dict['key1']['key2']) # {} print(type(example_dict['key1']['key2'])) # <class '__main__.Hasher'>
Note that this returns an empty Hasher when a key is missing.
Since Hasher is a subclass of dict , you can use Hasher in the same way you could use dict . All the same methods and syntax are available; Hashers simply handle missing keys differently.
You can convert a regular dict to Hasher as follows:
hasher = Hasher(example_dict)
and converting a Hasher to a regular dict just as easy:
regular_dict = dict(hasher)
Another alternative is to hide the ugliness in the helper function:
def safeget(dct, *keys): for key in keys: try: dct = dct[key] except KeyError: return None return dct
Thus, the rest of your code can remain relatively readable:
safeget(example_dict, 'key1', 'key2')