Vous pouvez utiliser getdeux fois:
example_dict.get('key1', {}).get('key2')
Cela reviendra Nonesi l'un key1ou l' autre key2n'existe pas.
Notez que cela peut toujours déclencher un AttributeErrorif example_dict['key1']existe mais n'est pas un dict (ou un objet de type dict avec une getméthode). Le try..exceptcode que vous avez publié lèverait un à la TypeErrorplace s'il example_dict['key1']est désinscriptible.
Une autre différence est que les try...exceptcourt-circuits immédiatement après la première clé manquante. La chaîne d' getappels ne le fait pas.
Si vous souhaitez conserver la syntaxe, example_dict['key1']['key2']mais ne voulez pas qu'elle déclenche jamais KeyErrors, vous pouvez utiliser la recette Hasher :
class Hasher(dict):
    # https://stackoverflow.com/a/3405143/190597
    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'>
Notez que cela renvoie un Hasher vide lorsqu'une clé est manquante.
Depuis Hasherune sous-classe de dictvous pouvez utiliser un Hasher de la même manière que vous pourriez utiliser un dict. Toutes les mêmes méthodes et syntaxe sont disponibles, les Hashers traitent simplement les clés manquantes différemment.
Vous pouvez convertir un régulier dicten un Hashercomme ceci:
hasher = Hasher(example_dict)
et convertissez a Hasheren régulier dicttout aussi facilement:
regular_dict = dict(hasher)
Une autre alternative est de cacher la laideur dans une fonction d'assistance:
def safeget(dct, *keys):
    for key in keys:
        try:
            dct = dct[key]
        except KeyError:
            return None
    return dct
Ainsi, le reste de votre code peut rester relativement lisible:
safeget(example_dict, 'key1', 'key2')