I got to say, I don’think I’ve EVER doen that – it is a very common pattern for a variable to be “unset” by being set to None, and if you need to check for that you use:
if var is None:
do_something_with_var
In fact, I’ve seen bugs where folks use:
if var:
do_something_with_var
and end up with errors when var happens to be falsy – there is a big difference between “anything Falsy” and “None”.
I don’t see any reason to treat things that are supposed to be None or a callable differently, even though it is very unliley that a callable will be Falsy. Not impossible mind you – a callable class could very will use bool for some other useful purpose – like indicating emptyness.
And it’s similar for:
if var == 0.0:
There is nothing inherently “Falsy” about zero – it all depends on what that value means – if I want to know if it’s unset, I check for None, if I want to know if its value is zero, I should check for that, just like I would check for its value being 5, or greater or less than some value.
I do like, and use, Truthiness to check for an empty container for the most part – as in my mind that is inherently Falsy.
This seems to me analogous to rarely using bare except: when you write a try:except block you should know what kind(s) of Exceptions you are prepared to handle, and you don’t want any other Exceptions to leak though. In an “if” block you should know what kinds of Truthiness you are expecting, and you don’t want other Truthy objects to leak through.
Now that I"ve written all that – I agree, this is not a discussion that belongs in ideas … Is there a “Pythonic style” forum?
The behaviour there is different (although probably sufficient for many use cases). if None is True: ... is a no-op, but boolif None: ... would be an error.
This discussion got me wondering, how exactly does static type checkers deal with the is operator? I’d guess they need to be the same type but a quick glance through the mypy docs didn’t provide me with answers.
I have seen some linters warn that you are using is when == is required I have seen warning.
% py3
Python 3.11.2 (v3.11.2:878ead1ac1, Feb 7 2023, 10:02:41) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
:>>> x = 93
:>>> if x is 93:
:... print('it is 93!')
:...
<stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
it is 93!
:>>> y = 93
:>>> if x is y:
:... print('it is 93!')
:...
it is 93!
:>>> x = 10374
:>>> y = 10374
:>>> if x is y:
:... print('it is y!')
:...
:>>>
This may work if 93 has been interned, but cannot be relied on.
It fails with 10374 in cpython as its not iterned.
The arguments to is don’t have to be the same type. [] is {} doesn’t raise an exception, it just returns False.
To a type-checker, there’s nothing special about is. It is an operator which accepts two operands of any type and returns a bool True/False flag. If it were a function, it would have this signature:
Slightly broadening the scope, having strict comparison operators in a small module somewhere (as functions, not keywords) might occasionally save a couple of keystrokes.
if strict_eq(expr, True): # TypeError if not a bool
pass
if strict_lt(expr, 1.0): # TypeError if not a float
pass
This is the sort of thing that really should be in your own personal toolkit, since what’s good for you is not the same as what’s good for other projects. It is equally “obvious” that your strict_eq function ought to test whether type(expr1) is type(expr2), and that it should test whether isinstance(expr1, type(expr2)), and that it should be given an explicit type that it tests against. All three are entirely valid (and I wouldn’t use any of them in my projects).
I think this is an interesting idea - I suppose perhaps this is more in the role of type checker than an assertion, so I would argue that assert_type may save the day again, for no runtime cost:
from typing import assert_type as at
if at(expr, bool): # type checker error if not a bool
pass
if at(expr, float) < 1.0: # type checker error if not a float
pass
And, for that matter, it seems even more obvious that it should return TypeError rather than ValueError (though I’m not aware of a counterargument to that one, unlike your examples).
I’d just like to point out that it’s hugely helpful in C# because conversion to bool is a bit awkward to spell. In Python, it’s spelled bool(), surrounding the expression.
Having to do something explicit to get a boolean value would also make it a lot easier for the inexperienced to understand the “truth value of an array is ambiguous” Numpy gotcha.
(I admit it’s not very convenient once and/or/not get involved.)
It’s still completely unnecessary to have to spell it at all, since there’s nothing an if statement can do with a value other than decide to execute, or decide to not-execute.
The numpy gotcha comes from a conflict between most of Python, where “empty list is false, non-empty list is true” and numpy, where operations get vectorized. The same would happen with an explicit boolification step, so all it’d do is add unnecessary boilerplate while keeping the ambiguity.