This blog post is triggered by a colleague stopping me in the hall and asking “What does ~
do in Python?” She was surprised by the behavior of the ~
operator when applied to Python bool types and I was surprised that it behaved differently on numpy bools than on Python bools. All in all enough surprises to write a short blog post about the difference between the two variable types.
The ~
Operator¶
Let’s start with the original question, what does ~n
do in Python? Answer: It inverts the bits of n
, where n
is an integer. So for example:
n = 85
print(" {0:d} in binary: {0:+b}".format(n))
print("~{0:d} in binary: {1:+b} is {1:d} in integer".format(n, ~n))
You may find it surprising that ~85
does not return the bit pattern 0101010
but this is just due to the two’s complement representation of integers in Python.
Python bool¶
Understanding two’s complement and knowing that Python bool
s are a subclass of int
, it is not surprising that
print(" True in binary: {:s}".format(bin(True)))
print("~True in binary: {:s} is {:d} in integer".format(bin(~True), ~True))
and so the truth value of ~True
is True
:
print(bool(~True))
because any integer other than zero evaluates to True
. This may come as a surprise if you are not aware that bools in Python are in fact integers, which use two’s complement. It’s even a little bit more confusing because
print(~False, bool(~False))
~False
in fact evaluates to True. If you want to correctly negate Python boolean values use logical not
and not bitwise not (~
):
print(not True, not False)
Numpy bool¶
What surprised me was that numpy bools show a different behavior:
import numpy as np
a = np.ones(10, dtype=bool)
print(a)
print(~a)
The reason for this is that numpy bools are an entirely different type. They are not an subclass of Python bools and they are also not a subclass of any numeric type. This is all clearly stated in the numpy reference manual even with the following warning
The bool_ type is not a subclass of the int_ type (the bool_ is not even a number type). This is different than Python’s default implementation of bool as a sub-class of int.
yet reading this without this example I didn’t fully understand the consequences.
In numpy we can make things even a little more convoluted if we mix Python bools and numpy.bool_ in an object array.
b = np.array([True, True, False, np.True_], dtype=object)
print(b.astype(np.bool))
print((~b).astype(np.bool))
My advise above to use logical not
also does not work for numpy arrays because not
is not applied element-wise but tries to evaluate the boolean value of the entire array.
print(not b)
The correct thing to do for numpy arrays is to use the ufunc logical_not
, which gives the expected result for both our arrays a
and b
print("Array a")
print(a)
print(np.logical_not(a))
print("\nArray b")
print(b)
print(np.logical_not(b))
This blog post is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. This post was written as a Jupyter Notebook in Python. You can download the original notebook.