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.