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.