import numpy as np
a = np.array([0])
b = np.array([None])
c = np.array([''])
d = np.array([' '])
为什么我们会有这种不一致:
>>> bool(a)
False
>>> bool(b)
False
>>> bool(c)
True
>>> bool(d)
False
import numpy as np
a = np.array([0])
b = np.array([None])
c = np.array([''])
d = np.array([' '])
为什么我们会有这种不一致:
>>> bool(a)
False
>>> bool(b)
False
>>> bool(c)
True
>>> bool(d)
False
正如Scalars中所解释的那样,我很确定答案是:
数组标量具有与 ndarrays 相同的属性和方法。[1] 这允许人们在与数组相同的基础上部分处理数组的项目,从而消除混合标量和数组操作时产生的粗糙边缘。
因此,如果调用标量是可以接受的,那么调用shape 数组bool
必须是可以接受的,因为它们尽可能是相同的。bool
(1,)
而且,虽然我所知道的文档中的任何地方都没有直接说明,但从设计中可以很明显地看出,NumPy 的标量应该像原生 Python 对象一样工作。
所以,这就解释了为什么np.array([0])
是虚假的而不是真实的,这就是你最初感到惊讶的地方。
所以,这解释了基础知识。但是案件的具体情况c
呢?
首先,请注意您的数组np.array([''])
不是一个包含一个 Pythonobject
的数组,而是一个包含一个长度为 1 的 NumPy<U1
空终止字符串的数组。固定长度字符串值与 Python 字符串具有不同的真实性规则——它们确实不能;对于固定长度的字符串类型,“如果为空则为假”没有任何意义,因为它们永远不会为空。你可以争论 NumPy 是否应该这样设计,但它显然始终遵循该规则,我认为相反的规则在这里不会更容易混淆,只是不同。
但是字符串似乎还有其他奇怪的事情发生。考虑一下:
>>> np.array(['a', 'b']) != 0
True
这不是将<U2
字符串与 0 进行元素比较并返回array([True, True])
(正如您从 得到的那样np.array(['a', 'b'], dtype=object)
),而是进行数组范围的比较并确定没有字符串数组等于 0,这似乎很奇怪……我不确定这是否值得在这里单独回答,甚至是整个单独的问题,但我很确定我不会成为写这个答案的人,因为我不知道这里发生了什么。:)
除了 shape 数组之外, shape(1,)
数组的()
处理方式相同,但其他任何东西都是 a ValueError
,因为否则很容易将数组与and
NumPy 无法自动转换为元素操作的其他 Python 运算符一起滥用。
我个人认为与其他数组保持一致比在这里与标量保持一致更有用——换句话说,只需提高一个ValueError
. 我还认为,如果在这里与标量一致很重要,那么与未装箱的 Python 值一致会更好。换句话说,如果bool(array([v]))
和bool(array(v))
将被允许,它们应该总是返回与 完全相同的东西bool(v)
,即使这与 不一致np.nonzero
。但我可以从另一个角度看待这个论点。
对于具有一个元素的数组,数组的真值由该元素的真值决定。
要说明的要点是,np.array([''])
它不是一个包含一个空 Python 字符串的数组。创建这个数组是为了保存每个恰好一个字节的字符串,并且 NumPy 用空字符填充太短的字符串。这意味着数组等于np.array(['\0'])
。
在这方面,NumPy 与评估bool('\0')
为True
.
事实上,False
NumPy 数组中唯一的字符串是不包含任何非空白字符('\0'
不是空白字符)的字符串。
该布尔评估的详细信息如下所示。
浏览 NumPy 迷宫般的源代码并不总是那么容易,但我们可以在arraytypes.c.src文件中找到控制不同数据类型中的值如何映射到布尔值的代码。这将解释如何确定bool(a)
、bool(b)
和。bool(c)
bool(d)
在我们进入该文件中的代码之前,我们可以看到调用bool()
NumPy 数组会调用内部_array_nonzero()
函数。如果数组为空,我们得到False
. 如果有两个或更多元素,我们会得到一个错误。但是如果数组只有一个元素,我们就打一行:
return PyArray_DESCR(mp)->f->nonzero(PyArray_DATA(mp), mp);
现在,PyArray_DESCR
是一个包含数组各种属性的结构。f
是指向另一个PyArray_ArrFuncs
保存数组nonzero
函数的结构的指针。换句话说,NumPy 将调用数组自己的特殊nonzero
函数来检查那个元素的布尔值。
确定一个元素是否为非零显然取决于元素的数据类型。实现特定类型的非零函数的代码可以在arraytypes.c.src文件的“非零”部分找到。
正如我们所料,浮点数、整数和复数False
是否等于零。这说明bool(a)
。在对象数组的情况下,None
同样会被评估为False
因为 NumPy 只是调用PyObject_IsTrue
函数。这说明bool(b)
。
为了理解 and 的结果bool(c)
,bool(d)
我们看到nonzero
字符串类型数组的函数被映射到STRING_nonzero
函数:
static npy_bool
STRING_nonzero (char *ip, PyArrayObject *ap)
{
int len = PyArray_DESCR(ap)->elsize; // size of dtype (not string length)
int i;
npy_bool nonz = NPY_FALSE;
for (i = 0; i < len; i++) {
if (!Py_STRING_ISSPACE(*ip)) { // if it isn't whitespace, it's True
nonz = NPY_TRUE;
break;
}
ip++;
}
return nonz;
}
(unicode 案例或多或少是相同的想法。)
因此,在具有字符串或 unicode 数据类型的数组中,字符串只有False
在它只包含空白字符时才是:
>>> bool(np.array([' ']))
False
对于问题中的数组c
,实际上有一个空字符\0
填充看似空的字符串:
>>> np.array(['']) == np.array(['\0'])
array([ True], dtype=bool)
该STRING_nonzero
函数看到这个非空白字符,所以bool(c)
是True
.
如本答案开头所述,这与 Python 对包含单个空字符的字符串的评估一致:bool('\0')
也是True
.
更新:Wim 已通过使仅包含空字符或仅包含空格和空字符的字符串评估为False
. 这意味着 NumPy 1.10+ 将看到bool(np.array(['']))
is False
,这更符合 Python 对“空”字符串的处理。
现在已经在master中修复了。
我认为这是一个错误,并且numpy
开发人员同意了,所以这个补丁今天早些时候被合并了。我们应该会在即将发布的 1.10 版本中看到新的行为。
Numpy 似乎遵循与内置 python ** 相同的转换,在这种情况下,它似乎是因为调用nonzero
. 显然len
也可以使用,但在这里,这些数组都不是空的(长度0
) - 所以这不是直接相关的。请注意,调用bool([False])
也True
根据这些规则返回。
a = np.array([0])
b = np.array([None])
c = np.array([''])
>>> nonzero(a)
(array([], dtype=int64),)
>>> nonzero(b)
(array([], dtype=int64),)
>>> nonzero(c)
(array([0]),)
bool
这似乎也与铸造的更多枚举描述一致——你的例子都被明确讨论过。
有趣的是,字符串数组似乎确实存在系统性不同的行为,例如
>>> a.astype(bool)
array([False], dtype=bool)
>>> b.astype(bool)
array([False], dtype=bool)
>>> c.astype(bool)
ERROR: ValueError: invalid literal for int() with base 10: ''
我认为,当 numpy 将某些内容转换为 bool 时,它使用该PyArray_BoolConverter
函数,而该函数又只是调用该PyObject_IsTrue
函数 --- 即内置 python 使用的完全相同的函数,这就是为什么numpy
结果如此一致的原因。