TL;DR:
if 和== 是完全不同的操作。 if 检查变量的真值,而 == 比较两个变量。 is 也会比较两个变量,但它会比较两个变量是否引用同一个对象。
因此,将变量与True、False 或None 进行比较以检查其真值是没有意义的。
当if 用于变量时会发生什么?
在 Python 中,像 if 这样的检查会隐式获取参数的 bool。所以
if something:
将(在后台)执行如下:
if bool(something):
请注意,您永远不应该在代码中使用后者,因为它被认为不那么 Python 并且速度较慢(因为 Python 使用了两个 bools:bool(bool(something)))。 始终使用if something。
如果您对 CPython 3.6 如何评估它感兴趣:
请注意,CPython 在这里并不完全使用hasattr。它会检查x 的type 是否实现了该方法,但没有通过__getattribute__ 方法(hasattr 会使用该方法)。
在 Python2 中,该方法被称为 __nonzero__ 而不是 __bool__
使用==比较变量时会发生什么?
== 将检查是否相等(通常也称为“值相等”)。但是,这种相等性检查不会强制操作数(与其他编程语言不同)。 Python 中的值相等是显式实现的。所以你可以这样做:
>>> 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
但是,如果两个操作数都没有实现比较,== 将默认为引用比较 (is)。这就是为什么:
>>> (None, ) == True
False
因为tuple 不“支持”与int 相等,反之亦然。请注意,即使将列表与元组进行比较也是“不支持的”:
>>> [None] == (None, )
False
如果您有兴趣,这就是 CPython (3.6) 如何实现相等(橙色箭头表示操作是否返回 NotImplemented 常量):
这只是大致正确,因为 CPython 还会在调用(如果存在)或跳过(如果它不存在)。
请注意,Python2 中的行为明显更长(至少在方法返回 NotImplemented 的情况下)并且 Python 2 也支持 __cmp__,
使用is比较变量时会发生什么?
is 通常称为引用相等比较运算符。如果两个变量都引用完全相同的对象,它只会返回True。通常,具有相同值的变量仍然可以引用不同的对象:
>>> 1 is 1. # same value, different types
False
>>> a = 500
>>> a is 500 # same value, same type, different instances
False
请注意,CPython 使用缓存值,因此有时“应该”是不同实例的变量实际上是同一个实例。这就是为什么我没有使用500 is 500(在同一行中具有相同值的文字总是相等)以及为什么我不能使用1 作为示例(因为CPython 重新使用值-5 到256)。
但回到你的比较:is 比较引用,这意味着如果两个操作数具有相同的类型和值但它们必须是相同的引用是不够的。鉴于它们甚至没有相同的类型(您将 tuple 与 bool 和 NoneType 对象进行比较),is 不可能返回 True。
请注意,True、False 和 None(还有 NotImplemented 和 Ellipsis)是 CPython 中的常量和单例。这不仅仅是在这些情况下的优化。