Python's in (__contains__) operator returns a bool whose value is neither True nor False

boolean, comparison-operators, membership, python

Solution

You are running into comparison operator chaining; `1 in () == False` does not mean `(1 in ()) == False`.

Rather, comparisons are chained and the expression really means:

(1 in ()) and (() == False)

Because `(1 in ())` is already false, the second half of the chained expression is ignored altogether (since `False and something_else` returns `False` whatever the value of `something_else` would be).

See the comparisons expressions documentation:

Comparisons can be chained arbitrarily, e.g., `x < y <= z` is equivalent to `x < y and y <= z`, except that `y` is evaluated only once (but in both cases `z` is not evaluated at all when `x < y` is found to be false).

For the record, `<`, `>`, `==`, `>=`, `<=`, `!=`, `is`, `is not`, `in` and `not in` are all comparison operators (as is the deprecated `<>`).

In general, don't compare against booleans; just test the expression itself. If you have to test against a boolean literal, at least use parenthesis and the `is` operator, `True` and `False` are singletons, just like `None`:

>>> (1 in ()) is False
True

This gets more confusing still when integers are involved. The Python `bool` type is a subclass of `int`1. As such, `False == 0` is true, as is `True == 1`. You therefor can conceivably create chained operations that almost look sane:

3 > 1 == True

is true because `3 > 1` and `1 == True` are both true. But the expression:

3 > 2 == True

is false, because `2 == True` is false.

1 `bool` is a subclass of `int` for historic reasons; Python didn't always have a `bool` type and overloaded integers with boolean meaning just like C does. Making `bool` a subclass kept older code working.

Problem

As expected, 1 is not contained by the empty tuple ``` >>> 1 in () False ``` but the `False` value returned is not equal to `False` ``` >>> 1 in () == False False ``` Looking at it another way, the `in` operator returns a `bool` which is neither `True` nor `False`: ``` >>> type(1 in ()) <type 'bool'> >>> 1 in () == True, 1 in () == False (False, False) ``` However, normal behaviour resumes if the original expression is parenthesized ``` >>> (1 in ()) == False True ``` or its value is stored in a variable ``` >>> value = 1 in () >>> value == False True ``` This behaviour is observed in both Python 2 and Python 3. Can you explain what is going on?

Original source