我今天在python if
条款中遇到了意外的结果:
import numpy
if numpy.allclose(6.0, 6.1, rtol=0, atol=0.5):
print 'close enough' # works as expected (prints message)
if numpy.allclose(6.0, 6.1, rtol=0, atol=0.5) is True:
print 'close enough' # does NOT work as expected (prints nothing)
经过一番探讨(即this question,特别是this answer)之后,我明白了原因:type
返回的numpy.allclose()
为numpy.bool_
而不是普通的bool
,显然如果foo = numpy.bool_(1)
,那么if foo
将评估为True
,而if foo is True
将评估为False
。这似乎是is
运营商的工作。
我的问题是:为什么numpy有自己的布尔类型,鉴于这种情况,最佳做法是什么?我可以通过编写if foo:
来获取上述示例中的预期行为,但我更喜欢更严格的if foo is True:
,因为它排除2
和[2]
之类的内容{1}},有时需要明确的类型检查。
答案 0 :(得分:16)
你正在做一些被认为是反模式的事情。引用PEP 8:
不要使用==。
将布尔值与True或False进行比较
Yes: if greeting:
No: if greeting == True:
Worse: if greeting is True:
numpy不是为了促进你的非pythonic代码而设计的,这不是numpy中的错误。事实上,它是为什么你的个人习语是一种反模式的完美例子。
正如PEP 8所说,使用is True
甚至比== True
更糟糕。为什么?因为你正在检查对象标识:不仅结果必须在布尔上下文中(通常只需要你),并且等于布尔True
值,它实际上必须是常数True
。很难想象这就是你想要的任何情况。
你特别不想在这里:
>>> np.True_ == True
True
>>> np.True_ is True
False
所以,你所做的只是明确地使你的代码与numpy不兼容,而各种其他C扩展库(可以想象一个纯Python库可以返回一个等于True
的自定义值,但我不知道知道任何这样做的事。)
在您的特定情况下,没有理由排除2
和[2]
。如果您阅读numpy.allclose
的文档,显然不会返回它们。但是考虑一些其他功能,就像标准库中的许多功能一样,只是说它们评估为真或假。这意味着他们明确被允许返回其中一个真实的论点,并且经常会这样做。你为什么要考虑那个假?
最后,为什么numpy或任何其他C扩展库定义了这样的bool兼容但不是bool类型?
通常,这是因为它们包装了C int或C ++ bool或其他类似的类型。在Numpy的情况下,它包含一个值,该值可以存储在最快的机器字类型或单个字节(在某些情况下甚至可能是一个位),以适应性能,并且您的代码不必关心哪个,因为所有表示看起来都是一样的,包括真实并且等于True
常数。
答案 1 :(得分:4)
为什么numpy有自己的布尔类型
空间和速度。 Numpy将东西存储在紧凑的数组中;如果它可以将布尔值拟合为单个字节,那么它将尝试。您不能轻易地使用Python对象执行此操作,因为您必须存储明显减慢计算速度的引用。
如果foo:我可以在上面的例子中得到预期的行为,但我喜欢更严格,如果foo是True:因为它排除了返回True的2和[2]之类的东西,有时是明确的类型检查是可取的。
好吧,不要那样做。