It does though, you can chain as many of these as you want:
some_dict.get("level_1_key", {}).get("level_2_key", {}).get("level_3_key", {})...
edit:>> but you still need to check for None with this code
> Not sure what you mean here. You only have to check for None if you use `.get("key")` and don't provide a fallback value.
GP was talking about `{"foo": None}` and trying to drill deeper, which I misunderstood. Still, a simple try/except allows you to short-circuit the deeply nested access.
>>> foo = {"foo": None}
>>> print(foo.get('foo', {}).get('bar'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'get'
>>> print((foo.get('foo') or {}).get('bar'))
None> Blowing up on `None` is an easy short-circuit.
The thing being discussed is attempting to access deeply nested values, so short-circuiting here is a win-win. I.e., you wouldn't want to unnecessarily traverse tons of empty dictionaries using the `.get() or {}` trick.
This has the side effect of replacing falsy values like False or 0 with an empty dictionary instead of giving you the actual value. For the exact intended behaviour, you do need to explicitly check for None instead of using a short circuit trick with or.
I would say the only reasonable choice is Optional[dict], in which case it should be sufficient, but Python being Python, it could be anything. And in these cases you need to handle all cases way more carefully.
I write 'may' because the difference between None and an empty dict may be very subtle and rarely does the API specify with enough precision what are supposed to be the semantics of each case.