17

if我今天在 python 子句中遇到了意想不到的结果:

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)

经过一番摸索(即这个问题,特别是这个答案),我明白了原因:type返回的numpy.allclose()不是numpy.bool_普通的 old bool,显然 if foo = numpy.bool_(1), thenif foo将评估为Truewhileif foo is True将评估为False。这似乎是is操作员的工作。

我的问题是:为什么 numpy 有自己的布尔类型,鉴于这种情况,最佳实践是什么?在上面的示例中,我可以通过编写if foo:来获得预期的行为,但我更喜欢更严格的,因为它排除了和return之if foo is True:类的东西,有时显式类型检查是可取的。2[2]True

4

2 回答 2

22

你正在做一些被认为是反模式的事情。引用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,但我不知道有什么这样做的) .


在您的特定情况下,没有理由排除2and [2]。如果您阅读 的文档numpy.allclose,它显然不会返回它们。但是考虑其他一些函数,比如标准库中的许多函数,它们只是说它们评估为真或假。这意味着他们被明确允许返回他们真实的论点之一,而且经常会这样做。为什么你会认为这是错误的?


最后,为什么 numpy 或任何其他 C 扩展库会定义这种 bool-compatible-but-not-bool 类型?

通常,这是因为它们包装了 C int 或 C++ bool 或其他一些此类类型。在 numpy 的情况下,它包装了一个值,该值可以存储在最快的机器字类型或单个字节(在某些情况下甚至可能是单个位)中以适应性能,并且您的代码不必关心哪个,因为所有表示看起来都一样,包括真实和等于True常数。

于 2013-09-20T17:22:34.973 回答
6

为什么numpy有自己的布尔类型

空间和速度。Numpy 将事物存储在紧凑的数组中;如果它可以将布尔值放入单个字节中,它会尝试。对于 Python 对象,您不能轻松地做到这一点,因为您必须存储引用,这会显着减慢计算速度。

我可以写 if foo: 在上面的例子中得到预期的行为,但我喜欢更严格的 if foo is True: 因为它排除了像 2 和 [2] 这样的东西返回 True,有时显式类型检查是可取的。

好吧,不要那样做。

于 2013-09-20T17:29:01.923 回答