
Hi Steven! On 21.09.22 13:17, Steven D'Aprano wrote:
The distinction you make between user-defined and non-user-defined classes doesn't hold water. If you allow that (say) `int|None` **might** be acceptable, then why would `Integer|None` **necessarily** be lazy and bad just because int is written in C and built into the intepreter while Integer is written in Python by a user?
The laziness/badness of `SomeType|None` must depend on the semantics of SomeType, not the implementation language or who wrote it.
"Our API was lazy and bad because it uses None, but then it got accepted into Python as a builtin, so now it is no longer lazy or bad." /s
I considered a while if "user-defined types/classes" is the right wording, but didn't find a better alternative. The point I'm making is that `X|None` could especially then hide bugs if X defines attributes that will often be accessed in a chain. For int or float, there are only a few methods, which are arguably rarely used and chaining is probably not used at all. Plus, providing nulltypes for primitives would feel very awkward. Consider ``` x: int | None = 5 # ... if x is not None: y = 1 << x.bit_cont() ``` The typo with bit_cont() is hidden whenever x is None, so it might be missed by unit tests with limited coverage. However, using a nullable variant of it would probably make it look like this: ``` class Int(int): # ... class NoneInt(Int): # ... x = Int(5) # With the option for NoneInt() # ... x_bit_count = x.bit_cont() if x_bit_count == NoneInt: y = 1 << x_bit_count ``` This is certainly not pretty for something basic as an int, plus there will most likely be a performance hit when replacing every int by Int(), not to mention compatibility issues with APIs/libraries expecting plain int. On the other hand, more complex classes like in the mentioned publisher API could indeed profit from such null-types, since a chain like ``` book?.publisher?.owner?.namme ``` will hide the typo with namme whenever any of book, publisher or owner is None, so there is much more potential here to miss it in a unit test. This is actually the distinction that I wanted to make, not in the technical sense if a class is defined in the standard library or not.
The problem is, that I never actually thought about his suggested way.
Some people have, and found that it doesn't always simplify the API that much, or at all. If you end up replacing `obj is None` with `obj == NULL` or `obj.isnull()` then you haven't really gained much.
Certainly. But without None-aware operators, the gain in readability can be large, if you can just write ``` name = book.publisher.owner.name if name is not None: # No NoneStr here, see above # ... ``` versus ``` if (book is not None) and (book.publisher is not None) and ...: # ... ``` Just to be clear: I've been thankful for the comment and explanation of Steve Dower, because I've never thought about it that way and now see constructs in my code, where such a concept really would have simplified things and improved robustness. For others to profit as well, I would be glad to see this mentioned in the docs, so that it can be found even when not actually looking for it. But I certainly don't want to say that this concept must be followed everywhere or that "X|None" is necessarily a bad thing. After all, I'd still love to see Python supporting such None-aware operators. Best regards, Philipp