为什么与 "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
不是唯一的布尔类型。图书馆可以自由定义自己的。 flag
是 numpy.bool_
的实例,它与 True
是不同的对象。 (NumPy 对此有充分的理由 - 使用他们自己的布尔类型允许他们提供更统一的 0 维值处理。这与 NumPy 也有自己的数字标量类型的原因相同。)
此外,is True
甚至没有发现您示例中的问题。它只是将一种无声的不当行为转变为另一种。 f("False")
打印 doit
是个问题,但 f("True")
默默地什么都不做也是个问题。两个版本的测试都不会产生实际的错误消息。
风格指南如下:
# 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
不是唯一的布尔类型。图书馆可以自由定义自己的。 flag
是 numpy.bool_
的实例,它与 True
是不同的对象。 (NumPy 对此有充分的理由 - 使用他们自己的布尔类型允许他们提供更统一的 0 维值处理。这与 NumPy 也有自己的数字标量类型的原因相同。)
此外,is True
甚至没有发现您示例中的问题。它只是将一种无声的不当行为转变为另一种。 f("False")
打印 doit
是个问题,但 f("True")
默默地什么都不做也是个问题。两个版本的测试都不会产生实际的错误消息。