为什么与 "is" 的布尔比较如此糟糕?

Why is a boolean comparison with "is" so terrible?

风格指南如下:

# Correct:
if greeting:

# Wrong:
if greeting == True:

# Worse:
if greeting is True:

查看 PEP 8,搜索词 "worse"

这是为什么?我习惯于尽可能明确地检查条件,使代码更具可读性,并捕捉偏差。

考虑这个函数:

def f(do_it):
   if do_it : print("doit")
   else : print("no don't") 

abuse/oversee 很容易出现意外行为

>>> f("False")
doit
>>> f([False])
doit

这是一个真正的问题,例如,当您正在检查可能无意中传递 if 子句的 return 值时。这可以通过使用 is 结构来避免。

显然 PEP 推荐有充分的理由,但它是什么?

在评论者的推动下,进一步研究使我得出以下发现:

if x:

调用x的class的__bool方法。该方法应该 return True 或 False,具体取决于对象认为自己是两者中的哪一个。

if x==True:

调用x的class的__eq方法。该方法应该能够将自身与 True(或 False)进行比较,并且 return True 或 False,视情况而定。

if x is True

两者都不调用。这将测试 x 是否是 "True" 对象。它完全绕过了 __eq 和 __bool 方法。

注意:我不是在问==is之间的区别。如果这就是你来这里的原因,请参阅 Is there a difference between "==" and "is"?

Why is this?

因为逻辑错了,一个category mistake。使用 is 你明确地执行身份检查,参考比较。但这不是您打算在这里做的。代码的 intent 是检查一个值是否为真(或者,更严格地说,True)。该值是否恰好驻留在内存中与常量 True 相同的地址不仅无关紧要,而且会主动分散注意力。

换句话说,代码的意图涉及表达式的 value,因此检查它的 value,而不是它的 参考身份.

在理论上,is表达了错误的东西。我们关心价值的真实性,而不是身份。在我们确实关心身份的极少数情况下,is 比较是合适的。

在实际层面上,is True 甚至没有像人们期望的那样工作:

In [1]: import numpy

In [2]: x = numpy.array([1, 2, 3])

In [3]: flag = x[0] == 1

In [4]: flag
Out[4]: True

In [5]: if flag: print('true')
true

In [6]: if flag is True: print('true')

In [7]:

我们比较了一个 1 和一个 1,得到了一个看起来像 True 的东西,但是 is 比较失败了。那是因为 bool 不是唯一的布尔类型。图书馆可以自由定义自己的。 flagnumpy.bool_ 的实例,它与 True 是不同的对象。 (NumPy 对此有充分的理由 - 使用他们自己的布尔类型允许他们提供更统一的 0 维值处理。这与 NumPy 也有自己的数字标量类型的原因相同。)

此外,is True 甚至没有发现您示例中的问题。它只是将一种无声的不当行为转变为另一种。 f("False") 打印 doit 是个问题,但 f("True") 默默地什么都不做也是个问题。两个版本的测试都不会产生实际的错误消息。