Sometimes you do want some flow to terminate if a schema / data contract is breached, and not have this broken data contract propagate elsewhere / outwards.
And/or, it is a perfectly valid pattern to indeed add try..catch, and contain (hence "localize") the issue there and then. If there is a way to handle this breach - you will need some kind of if check anyway. Catching by matching against the particular KeyError exception is in fact robust - and if you forgot to add an intended catch, then the exception will propagate upstack, yes - which is what you want - and this way you do not need to worry about forgetting an "if None" check.
This sort of approach makes it structurally impossible to "forget" - if exception is not caught, it will abort the cannot-be-null breach continuing with program flow, possibly (e.g.) ending up persisted in some place it shouldn't be, causing problems later.
Use a defaultdict if you must have all keys optional. Using get() everywhere is buggy because it masks illegal key use.
Double lookup.
Cheaper than get() masking bugs in your code or dict generation.
But yes let's prematurely optimise for speed in a slow-by-default language by writing bug prone non-idiomatic code. OK.
Both have valid use cases. It's weird to be sarcastic about it.