[Python-ideas] Changing the meaning of bool.__invert__

Oscar Benjamin oscar.j.benjamin at gmail.com
Thu Apr 7 08:19:38 EDT 2016


On 7 April 2016 at 08:46, Antoine Pitrou <solipsis at pitrou.net> wrote:
>
> Hello,
>
> Booleans currently have reasonable overrides for the bitwise binary
> operators:
>
>>>> True | False
> True
>>>> True & False
> False
>>>> True ^ False
> True
>
> However, the same cannot be said of bitwise unary complement, which
> returns rather useless integer values:
>
>>>> ~False
> -1
>>>> ~True
> -2

This is all just a consequence of the (unfortunate IMO) design that
treats True and False as being equivalent to 1 and 0.

> Numpy's boolean type does the more useful (and more expected) thing:
>
>>>> ~np.bool_(True)
> False

This is a consequence of another unfortunate design by numpy. The
reason for this is that numpy uses Python's bitwise operators to do
element-wise logical operations. This is because it is not possible to
overload Python's 'and', 'or', and 'not' operators. So if I write:

>>> import numpy as np
>>> a = np.array([1, 2, 3, 4, 5, 6])
>>> a
array([1, 2, 3, 4, 5, 6])
>>> 1 < a
array([False,  True,  True,  True,  True,  True], dtype=bool)
>>> a < 4
array([ True,  True,  True, False, False, False], dtype=bool)
>>> (1 < a) & (a < 4)
array([False,  True,  True, False, False, False], dtype=bool)
>>> (1 < a) and (a < 4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is
ambiguous. Use a.any() or a.all()

The problem is that Python tries to shortcut this expression and so it
calls bool(1 < a):

>>> bool(1 < a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is
ambiguous. Use a.any() or a.all()

Returning a non-bool from __bool__ is prohibited which implicitly
prevents numpy from overloading an expression like `not a`:

>>> class boolish:
...     def __bool__(self):
...         return "true"
...
>>> bool(boolish())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __bool__ should return bool, returned str
>>> not boolish()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __bool__ should return bool, returned str

Because of this numpy uses &,|,~ in place of and,or,not for numpy
arrays and by extension this occurs for numpy scalars as you showed.

I think the numpy project really wanted to use the normal Python
operators but no mechanism was available to do it. It would have been
nice to use &,|,~ for genuine bitwise operations (on e.g. unsigned int
arrays). It also means that chained relations don't work e.g.:

>>> 1 < a < 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is
ambiguous. Use a.any() or a.all()

The recommended way is (1 < a) & (a < 4) which not as nice and
requires factoring a out if a is actually an expression like sin(x)**2
or something.

--
Oscar


More information about the Python-ideas mailing list