Apparently the reason this happens is that True, False, and None are
compared using 'is' in structural pattern matching (see
https://peps.python.org/pep-0634/#literal-patterns).
There's no way NumPy could avoid this. First off, Python won't even
let you subclass bool:
>>> class mybool(bool):
... pass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type 'bool' is not an acceptable base type
np.bool_ objects *do* compare equal to bool:
>>> type(np.array(1) > 0)
<class 'numpy.bool_'>
>>> (np.array(1) > 0) == True
True
but that doesn't matter because True is specifically special cased in
structural pattern matching.
The workaround is to use np.True_ and np.False_ in your pattern
>>> match a > 1:
... case np.True_ | np.False_:
... print('python float')
... case _:
... print('Huh?: numpy float')
python float
Fortunately, since these compare equal to Python bool, this will also
work even if a > 1 is a normal True or False:
>>> a = 1
>>> import numpy as np
... a = np.float64(1)
... assert isinstance(a, float)
... match a > 1:
... case np.True_ | np.False_:
... print('python float')
... case _:
... print('Huh?: numpy float')
python float
Aaron Meurer
On Thu, Jun 27, 2024 at 3:33 PM Stefano Miccoli via NumPy-Discussion
<[email protected]> wrote:
>
> It is well known that ‘np.bool' is not interchangeable with python ‘bool’,
> and in fact 'issubclass(np.bool, bool)’ is false.
>
> On the contrary, numpy floats are subclassing python
> floats—'issubclass(np.float64, float) is true—so I’m wondering if the fact
> that scalar comparison returns a np.bool breaks the Liskov substitution
> principle. In fact ’(np.float64(1) > 0) is True’ is unexpectedly false.
>
> I was hit by this behaviour because in python structural pattern matching,
> the ‘a > 1’ subject will not match neither ’True’ or ‘False’ if ‘a' is a
> numpy scalar: In this short example
>
> import numpy as np
> a = np.float64(1)
> assert isinstance(a, float)
> match a > 1:
> case True | False:
> print('python float')
> case _:
> print('Huh?: numpy float’)
>
> the default clause is matched. If we set instead ‘a = float(1)’, the first
> clause will be matched. The surprise factor is quite high here, in my opinion.
> (Let me add that ‘True', ‘False', ‘None' are special in python structural
> pattern matching, because they are matched by identity and not by equality.)
>
> I’m not sure if this behaviour can be avoided, or if we have to live with the
> fact that numpy floats are to be kept well contained and never mixed with
> python floats.
>
> Stefano_______________________________________________
> NumPy-Discussion mailing list -- [email protected]
> To unsubscribe send an email to [email protected]
> https://mail.python.org/mailman3/lists/numpy-discussion.python.org/
> Member address: [email protected]
_______________________________________________
NumPy-Discussion mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/numpy-discussion.python.org/
Member address: [email protected]