【问题标题】:boolean and type checking in python vs numpypython vs numpy中的布尔和类型检查
【发布时间】:2013-09-26 04:11:24
【问题描述】:

我今天在 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),我明白了原因:numpy.allclose() 返回的typenumpy.bool_,而不是普通的旧bool,显然如果foo = numpy.bool_(1),则if foo 将评估为True,而if foo is True 将评估为False。这似乎是 is 操作员的工作。

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

【问题讨论】:

  • 您使用的是 Python 2,其中 True 不是关键字,因此 True 可能已被重新定义。因此,is True 不一定能证明任何类型,因为True 可能是("not quite True", 7)if foo is (True is True): 或其他东西应该可以工作,不过..(鸭子和奔跑)
  • @DSM: 确实如此(但is 它是True?),但我认为这不是他所做的最严重的问题。此外,虽然您的技巧可以解决任何返回原始 True 的函数的问题(来自不同模块的全局变量、来自闭包或本地副本、来自使用 Py_True 的 C 扩展等),但它会 break 返回当前全局True 的任何函数。为了调试它,我认为你需要阅读 Kripke。 :)

标签: python numpy boolean pep8


【解决方案1】:

你正在做一些被认为是反模式的事情。引用PEP 8:

不要使用 == 将布尔值与 True 或 False 进行比较。

Yes:   if greeting:
No:    if greeting == True:
Worse: if greeting is True:

numpy 的设计目的不是为了方便您的非 Python 代码,这一事实并不是 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-compatible-but-not-bool 类型?

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

【讨论】:

  • 实际上,传统上我认为if greeting is Trueif greeting == True 更好;当您想接受真实但不真实的价值观时,这是一种罕见的情况,但仍然是合理的。
  • 仅供参考,您引用的是 PEP8 的一部分 controversial
  • 引用that same link:“我同意一般建议是 if cond: 和 if not cond:,这对于 95% 的用例来说已经足够了。但是也有一些(罕见)我们想用布尔值检查身份的情况......”(继续举例)。
  • 在这个答案和上面的评论中反复呼吁 PEP8 过于敌对,可以说是居高临下,坦率地说是不必要的。 NumPy 开发人员自己已经公开承认 NumPy 未能利用标准Python 标量类型是一个众所周知的(尽管不可避免的)设计缺陷,不存在任何实用的 NumPy 端解决方案:"...if we had a choice, I think that (i.e., use standard Python scalar types) may be what we would do (or abolish the scalars completely effectively doing the same)."
  • 相切地,有 许多 个有效的理由可以与 TrueFalse 单例进行显式比较。隐式布尔比较会引入具有非布尔真实性的边缘情况(例如,整数 0 和空字符串到False 的静默强制),因此违反了“显式优于隐式”。 PEP 20 的格言——在这方面可以说胜过 PEP 8。例如,在测试外部调用者定义的数据或来自可疑来源的输入时,假设合理的布尔真实性通常会增加攻击面。
【解决方案2】:

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

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

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

好吧,不要那样做。

【讨论】:

    猜你喜欢
    • 2017-11-05
    • 2012-11-28
    • 2018-01-31
    • 1970-01-01
    • 2013-07-22
    • 2013-06-05
    • 2014-03-09
    • 1970-01-01
    • 2010-09-13
    相关资源
    最近更新 更多