I suspect people will use these comments to share their wishlist of Python features, so let me add that I really wish Python had a safe navigation operator, for when you are dealing with nested objects that could be None. I have been trying to parse a lot of XML and JSON in Python lately and a feature like that could really help reduce boilerplate checks.
The syntax has a bit of a learning curve, but there's a library called glom that can be helpful in parsing heterogeneous JSON. Specifically it has a coalesce operator that let's you define multiple paths to get the data you are trying to get at, with a default value if none of the paths are valid.
I haven't even used Python for like four years now, but all of these suggestions to do try except and get.key() or {} seem to be ignoring that Python has had a defaultdict in the standard library for, I don't even know, at least the last decade and a half, as long as I've been paying attention. It's even written in C: https://github.com/python/cpython/blob/d9246c7b734b8958da034....
Of course this only works at one level and not arbitrarily deeply, but you still need to check for None with this code; it may actually be better to write `x.get("key") or {}` so that you always get an empty dict.
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.
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.
The problem was underspecified, but I suggested that under the assumption that the value would be an Optional[dict]. If you have another falsy value, it means either Optional[Any] or something like Optional[Union[dict, list]] (I put list as an example, but you get the idea).
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.
Yeah, when trying to drill down into deeply nested structures, it'd be a waste to just blindly nullish-coalesce any None objects into empty dicts instead of just immediately short-circuiting out of the deep access. It's a cute trick but would not pass code review in my shop.
>> 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.
The issue is if the dictionary has a key defined and the value is None. Your get expression will return None, causing your next access to raise an error.
Ah, I see. Still, I'd just catch that exception and move on because it's obvious you won't have anything more deeply nested anyway. 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.
You're right. Python is pass by reference and it's the reference being changed, not the object. This is what happens when you read code on the internet after drinks.
I did type it on my phone, so I’m sure it needs a little correction (particularly the choice of exceptions is not optimal, I think) but feel free to run it in any recent python interpreter and return back with any mutation you see.
It's been stuck in discussions for a long time, unfortunately. The main reason that I remember gleaning from discussions is it's unclear just how "special" None should be treated as, and whether having special syntax for it is excessively "special".
I saw some suggestions for overridable behaviour in the data model for None-like objects, but didn't scrutinise them closely. Either way, it seems controversial. On the one hand, the ability to override operators is really key to Python's design, but on the other it could lead to rather surprising behaviour from overeager overriding. (But to be fair, this is true of other, more common operators too.)
So, for now we're stuck with `if _ is not None else`, I suppose!
That's something I've wished for a long time that it had. I know you can use exceptions to get similar behavior but it's really ugly and I just generally don't like raising exceptions for cases that aren't actually exceptional (even though I know Python uses exceptions for normal flow with StopIteration).
What's actually a bit odd about Python missing that operator is that its `or` operator acts as a rough equivalent of C#'s null-coalescing (`??`) operator. To me the safe navigation operator goes hand-in-hand with that.
> What's actually a bit odd about Python missing that operator is that its `or` operator acts as a rough equivalent of C#'s null-coalescing (`??`) operator.
Python `or` is C# `||` plus type coercion. It is not even roughly `??` because important non-None Python values are falsey (0, False, empty collections).
It evaluates to the left if that's truthy otherwise it evaluates to the right, similar to how `??` evaluates to the left if that's non-null otherwise the right.
In C# even if `||` automatically coerced the types, the output will always be a bool.
https://en.wikipedia.org/wiki/Safe_navigation_operator