On Wed, Nov 02, 2016 at 10:22:56PM -0400, Kyle Lahnakoski wrote:
On 11/2/2016 2:30 PM, Zero Piraeus wrote:
If I write something like obj.attr, the failure mode I care about is that obj has no attribute attr, rather than that obj is specifically None (or one of a defined group of somewhat Nonelike objects).
I agree with this understanding. The problem with None-coalescing is it doesn't consider where None came from.
I don't see why you think that's a problem. What does it matter where None comes from? It seems to me that the important factor is what you intend to do with it, not where it came from.
If I write this code:
None if obj is None else obj.attr
what difference does it make where None came from? The important thing is that I'm treating obj == None as a known condition.
Now if you *don't* want to treat None specially, then of course this feature will be of no interest to you. This feature isn't aimed at you, it is aimed at the many people who do treat None specially.
If you prefer:
# obj is known to be a specific type, but attr may be None None if obj.attr is None else obj.attr.flange()
That would be written
in the proposed new syntax, and obj.attr will only be evaluated once.
But note that this is quite different from the situation that Zero is talking about. In Zero's code, he prefers for obj.attr to not exist at all rather than setting it to None. So rather than this:
None if obj.attr is None else obj.attr.flange()
he might write something similar to this:
None if not hasattr(obj, 'attr') else obj.attr.flange()
(or at least *I* might write that, under the circumstances Zero refers to.) And here's the good bit: under this proposal, we might simplify it to this:
getattr(obj, 'attr', None)?.flange()
which handles three cases for the price of one:
- obj.attr doesn't exist at all, in which case return None; - obj.attr exists and is None, in which case return None; - obj.attr exists and is not None, in which case call its flange() method.
To me, having a compact and expressive syntax to write that is very attractive.
I suspect enumerating the source of None values will reveal the majority of them are a result of `getattr(obj, attr)` returning None (or obj.get(attr) returning None).
Have you read the PEP? If you do, you'll see that the author has already collected potential examples of use from the standard library:
Looking at those examples of code, I don't think it is likely that the majority (or even a large minority) are the result of getattr.
But even if they are, what difference does it make?
If your code deals with annotated objects, rather than strictly typed objects, you will have many instances of attributes resolving to None. Making specific classes for each of the annotations, or combinations of annotations, is prohibitive, so you don't. Rather, you use a few general types and set some attributes to None to indicate a property is not-relevant-for-this-object.
I'm afraid I don't understand this.
None checks are type checks. They are late type checks .
Call it a type check if you like, but type check or value check or "rhubarb", whatever you want to call it, any time you test for None:
if expression is None ...
there's a reasonable chance that the None-aware operators may help you write better, more expressive code without falling into the trap of using `or` to handle None. Not in *all* cases, but I think often enough.