TL;DR:
if
and ==
are completely different operations. The if
checks the truth value of a variable while ==
compares two variables. is
also compares two variables but it compares if both reference the same object.
So it makes no sense to compare a variable with True
, False
or None
to check it's truth value.
What happens when if
is used on a variable?
In Python a check like if
implicitly gets the bool
of the argument. So
if something:
will be (under the hood) executed like:
if bool(something):
Note that you should never use the latter in your code because it's considered less pythonic and it's slower (because Python then uses two bool
s: bool(bool(something))
). Always use the if something
.
If you're interested in how it's evaluated by CPython 3.6:

Note that CPython doesn't exactly use hasattr
here. It does check if the type
of x
implements the method but without going through the __getattribute__
method (hasattr
would use that).
In Python2 the method was called __nonzero__
and not __bool__
What happens when variables are compared using ==
?
The ==
will check for equality (often also called "value equality"). However this equality check doesn't coerce the operands (unlike in other programming languages). The value equality in Python is explicitly implemented. So you can do:
>>> 1 == True # because bool subclasses int, True is equal to 1 (and False to 0)
True
>>> 1.0 == True # because float implements __eq__ with int
True
>>> 1+1j == True # because complex implements __eq__ with int
True
However ==
will default to reference comparison (is
) if the comparison isn't implemented by either operand. That's why:
>>> (None, ) == True
False
Because tuple
doesn't "support" equality with int
and vise-versa. Note that even comparing lists to tuples is "unsupported":
>>> [None] == (None, )
False
Just in case you're interested this is how CPython (3.6) implements equality (the orange arrows indicate if an operation returned the NotImplemented
constant):

That's only roughly correct because CPython also checks if the type()
of value1
or value2
implements __eq__
(without going through the __getattribute__
method!) before it's called (if it exists) or skipped (if it doesn't exist).
Note that the behavior in Python2 was significantly more lengthy (at least if the methods returned NotImplemented
) and Python 2 also supported __cmp__
,
What happens when variables are compared using is
?
is
is generally referred to as reference equality comparison operator. It only returns True
if both variables refer to exactly the same object. In general variables that hold the same value can nevertheless refer to different objects:
>>> 1 is 1. # same value, different types
False
>>> a = 500
>>> a is 500 # same value, same type, different instances
False
Note that CPython uses cached values, so sometimes variables that "should" be different instances are actually the same instance. That's why I didn't use 500 is 500
(literals with the same value in the same line are always equal) and why I couldn't use 1
as example (because CPython re-uses the values -5 to 256).
But back to your comparisons: is
compares references, that means it's not enough if both operands have the same type and value but they have to be the same reference. Given that they didn't even have the same type (you're comparing tuple
with bool
and NoneType
objects) it's impossible that is
would return True
.
Note that True
, False
and None
(and also NotImplemented
and Ellipsis
) are constants and singletons in CPython. That's not just an optimization in these cases.