# Python “bool” — wat?

## what I learned about bool while implementing it for Batavia

The *bool* type for Python was proposed in PEP 285 by Guido van Rossum, and adopted in Python 2.3.

This PEP proposes the introduction of a new built-in type, bool,

with two constants, False and True. The bool type would be a

straightforward subtype (in C) of the int type, and the values

False and True would behave like 0 and 1 in most respects

Under the Review section of PEP 285, the questions of arithmetic using *bool*s and *bool* inheriting from *int* were addressed:

4) Should we strive to eliminate non-Boolean operations on bools

in the future, through suitable warnings, so that for example

True+1 would eventually (in Python 3000) be illegal?

=> No.

There's a small but vocal minority that would prefer to see

"textbook" bools that don't support arithmetic operations at

all, but most reviewers agree with me that bools should always

allow arithmetic operations.

6) Should bool inherit from int?

=> Yes.

In an ideal world, bool might be better implemented as a

separate integer type that knows how to perform mixed-mode

arithmetic. However, inheriting bool from int eases the

implementation enormously (in part since all C code that calls

PyInt_Check() will continue to work -- this returns true for

subclasses of int). Also, I believe this is right in terms of

substitutability: code that requires an int can be fed a bool

and it will behave the same as 0 or 1. Code that requires a

bool may not work when it is given an int; for example, 3 & 4

is 0, but both 3 and 4 are true when considered as truth

values.

That’s Python’s BDFL writing, so that’s what happened. Most of the time, nothing unusual happens as a result of these design decisions. This blog post is about the **other** times.

#### Pop quiz

If you’d like to test your own knowledge of how *bool* behaves, do this 15-question quiz now, and then come back here and keep reading. (Note that the quiz specifies Python 3.5; some of the more interesting results are different in Python 3 compared with Python 2.)

Did you find the quiz easy? At the time of writing, with over 100 responses to the quiz, the average score is only 44%, and the highest score is 80%.

Let’s look at those questions and try to understand the *“wat‽”* in each one.

#### 1. True + True

Answer: *2*

This result comes directly from the representation of *True* as 1, and *bool*’s inheritance from *int*: 1 + 1 = 2*.*

#### 2. True – True

Answer: *0*

Same explanation as for question 1: 1 – 1 = 0.

Note that the font that medium.com uses makes it tricky to distinguish the digit zero (“o”) from the lowercase letter “o”; in this article any “0” not part of a word is a zero.

#### 3. True % 1

Answer: *0*

The modulo operator *%* gives the remainder when divided by 1, which is always 0 for integers: 1 % 1 = 0.

#### 4. True % 2

Answer: *True*

Our first real *wat*. 1 % 2 = 1, so why does Python return *True* instead?

Why does the type of the result change just because the value (not type) of one of the operands changes? I’ll pause that thought for a minute and pick it up after the next couple of questions.

#### 5. False % 1

Answer: *False*

#### 6. False % 2

Answer: *False*

At least 5 and 6 are consistent: 0 % 1 = 0 and 0 % 2 = 0, only Python is returning a *bool* for both, just like it did for question 4.

My guess is that there’s some kind of short-circuit evaluation going on, which returns the first operand of “a % b” without doing any type-casting if these conditions are met:

- a >= 0 and b > 0
- a < b

I’m not an expert on the CPython internals, so I haven’t been able to confirm this by digging around the source. (If you can confirm or deny this guess, please leave a comment.)

#### 7. False % 1.0

Answer: *0.0*

Since one of the operands is a float, *False* is type-cast to a float before the operation, and the result is also a float.

#### 8. True % True

Answer: *0*

This is really just question 3 again.

#### 9. False % True

Answer: *False*

This is question 4 again.

The rest of the questions are more about complex numbers and division by zero exceptions, and many more people got those questions correct, so I’ll only cover them briefly.

#### 10. False / 1j

Answer: *0j*

In case you haven’t seen it before, “1j” is an imaginary literal, used to build up values of the *complex* type. In mathematics, *j = sqrt(–1)*, a number that isn’t a member of the set of real numbers.

There’s no real trick to this question (pun intended); *False* is type-cast to *complex*, so the result will be of type *complex*.

#### 11. True / –1j

Answer: *(–0+1j)*

All the possible answers to this question that were given are effectively equal, so the question was asking how Python represents complex numbers, and under what circumstances the real part of a complex number would be “–0” instead of “0” (positive zero).

- The real and imaginary parts of
*complex*are always of type f*loat*, so Python doesn’t bother to add “.0” to numbers that appear to be integral. - Python’s f
*loat*implementation distinguishes 0 from –0, and the sign is negative if the operands have different signs. (Compare the case with f*loat*s: “0.0 / –1.0 = –0.0”.)

#### 12. False / –1j

Answer: *(–0–0j)*

The explanation for question 11 applies here too.

#### 13. True // False

Answer: *ZeroDivisionError: integer division or modulo by zero*

#### 14. 0.0 / False

Answer: *ZeroDivisionError: float division by zero*

#### 15. 0.0 // False

Answer: *ZeroDivisionError: float divmod()*

There’s no tricks here, just differently worded exception messages to choose from.

#### Conclusion

Just considering questions 1 to 9, which were the ones that dealt primarily with *bool*, the average score on the quiz was just 22%, and the highest score was 56%, both of which are much worse results than for the quiz overall.

From this I conclude that most people who answered the quiz don’t really understand the behaviour of Python’s *bool* type very well — but considering that the questions dealt mainly with obscure edge cases, it doesn’t really matter. 😛