Python's in (__contains__) operator returns a bool whose value is neither True nor False
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?
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 tox < y and y <= z
, except thaty
is evaluated only once (but in both casesz
is not evaluated at all whenx < 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.
1bool
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.