正如我所指出的in another answer,numpy 中的类型转换非常复杂,这是您所看到行为的根本原因。该答案中链接的文档清楚地表明,标量(/0d 数组)和 1d 数组在类型转换方面不同,因为后者不被视为逐个值。
您已经知道问题的前半部分:问题是您的两种情况的类型转换发生方式不同:
>>> (a + b).dtype
dtype('float64')
>>> (a + b[0]).dtype
dtype('float32')
>>> (a[0] + b[0]).dtype
dtype('float64')
如果我们考虑类型转换表,我相信我们可以理解您的示例中发生了什么:
>>> from numpy.testing import print_coercion_tables
can cast
[...]
In these tables, ValueError is '!', OverflowError is '@', TypeError is '#'
scalar + scalar
+ ? b h i l q p B H I L Q P e f d g F D G S U V O M m
? ? b h i l q l B H I L Q L e f d g F D G # # # O ! m
b b b h i l q l h i l d d d e f d g F D G # # # O ! m
h h h h i l q l h i l d d d f f d g F D G # # # O ! m
i i i i i l q l i i l d d d d d d g D D G # # # O ! m
l l l l l l q l l l l d d d d d d g D D G # # # O ! m
q q q q q q q q q q q d d d d d d g D D G # # # O ! m
p l l l l l q l l l l d d d d d d g D D G # # # O ! m
B B h h i l q l B H I L Q L e f d g F D G # # # O ! m
H H i i i l q l H H I L Q L f f d g F D G # # # O ! m
I I l l l l q l I I I L Q L d d d g D D G # # # O ! m
L L d d d d d d L L L L Q L d d d g D D G # # # O ! m
Q Q d d d d d d Q Q Q Q Q Q d d d g D D G # # # O ! m
P L d d d d d d L L L L Q L d d d g D D G # # # O ! m
e e e f d d d d e f d d d d e f d g F D G # # # O ! #
f f f f d d d d f f d d d d f f d g F D G # # # O ! #
d d d d d d d d d d d d d d d d d g D D G # # # O ! #
g g g g g g g g g g g g g g g g g g G G G # # # O ! #
F F F F D D D D F F D D D D F F D G F D G # # # O ! #
D D D D D D D D D D D D D D D D D G D D G # # # O ! #
G G G G G G G G G G G G G G G G G G G G G # # # O ! #
S # # # # # # # # # # # # # # # # # # # # # # # O ! #
U # # # # # # # # # # # # # # # # # # # # # # # O ! #
V # # # # # # # # # # # # # # # # # # # # # # # O ! #
O O O O O O O O O O O O O O O O O O O O O O O O O ! #
M ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
m m m m m m m m m m m m m m # # # # # # # # # # # ! m
scalar + neg scalar
[...]
array + scalar
+ ? b h i l q p B H I L Q P e f d g F D G S U V O M m
? ? b h i l q l B H I L Q L e f d g F D G # # # O ! m
b b b b b b b b b b b b b b e f d g F D G # # # O ! m
h h h h h h h h h h h h h h f f d g F D G # # # O ! m
i i i i i i i i i i i i i i d d d g D D G # # # O ! m
l l l l l l l l l l l l l l d d d g D D G # # # O ! m
q q q q q q q q q q q q q q d d d g D D G # # # O ! m
p l l l l l l l l l l l l l d d d g D D G # # # O ! m
B B B B B B B B B B B B B B e f d g F D G # # # O ! m
H H H H H H H H H H H H H H f f d g F D G # # # O ! m
I I I I I I I I I I I I I I d d d g D D G # # # O ! m
L L L L L L L L L L L L L L d d d g D D G # # # O ! m
Q Q Q Q Q Q Q Q Q Q Q Q Q Q d d d g D D G # # # O ! m
P L L L L L L L L L L L L L d d d g D D G # # # O ! m
e e e e e e e e e e e e e e e e e e F F F # # # O ! #
f f f f f f f f f f f f f f f f f f F F F # # # O ! #
d d d d d d d d d d d d d d d d d d D D D # # # O ! #
g g g g g g g g g g g g g g g g g g G G G # # # O ! #
F F F F F F F F F F F F F F F F F F F F F # # # O ! #
D D D D D D D D D D D D D D D D D D D D D # # # O ! #
G G G G G G G G G G G G G G G G G G G G G # # # O ! #
S # # # # # # # # # # # # # # # # # # # # # # # O ! #
U # # # # # # # # # # # # # # # # # # # # # # # O ! #
V # # # # # # # # # # # # # # # # # # # # # # # O ! #
O O O O O O O O O O O O O O O O O O O O O O O O O ! #
M ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
m m m m m m m m m m m m m m # # # # # # # # # # # ! m
[...]
以上是基于价值的促销的当前促销表的一部分。它表示在配对给定类型的两个 numpy 对象时,不同类型对结果类型的贡献程度(具体类型请参见第一列和第一行)。类型要根据single-character dtype specifications(“单字符字符串”下方)来理解,特别是np.dtype('f')对应np.float32(f代表C风格的float)和np.dtype('d')(d代表C风格double) 到np.float64(另见np.typename('f') 和'd' 相同)。
我在上面的表格中用粗体标出了两个项目:
标量 f + 标量 d --> d
数组 f + 标量 d --> f
现在让我们看看您的案例。前提是你有一个'f'数组a和一个'd'数组b。 a 只有一个元素这一事实并不重要:它是一个长度为 1 的一维数组,而不是一个 0d 数组。
-
当您执行a > b 时,您正在比较两个数组,这在上表中没有表示。我不确定这里的行为是什么;我的猜测是a 被广播到b 的形状,然后它的类型被转换为'd'。我认为这是因为np.can_cast(a, np.float64) 是True 而np.can_cast(b, np.float32) 是False。但这只是一个猜测,numpy 中的很多这种机制对我来说并不直观。
-
当您执行a > b[0] 时,您将'f' 数组与'd' 标量进行比较,因此根据上述内容,您将得到一个'f' 数组。这就是(a + b[0]).dtype 告诉我们的。 (当您使用a > b[0] 时,您看不到转换步骤,因为结果始终是布尔值。)
-
当您执行a[0] > b[0] 时,您会将'f' 标量与'd' 标量进行比较,因此根据上述内容,您将得到'd' 标量。这是(a[0] + b[0]).dtype 告诉我们的。
所以我相信这一切都与 numpy 中类型转换的怪癖一致。虽然在双精度和单精度中 0.4 的值看起来像是一个不幸的极端情况,但此功能更深入,并且问题作为一个大红色警告,在混合不同的 dtype 时应该非常小心。
最安全的做法是自己转换类型,以控制代码中发生的情况。尤其是在讨论重新考虑类型提升的某些方面。