为什么把一个复数和一个np.float64值相加时,得到的结果是复数,而不是按照数值提升规则应该得到的np.complex128类型?
我在阅读关于 NumPy的数值提升。我把主图贴在这里:
输入的dtype中更高kind的那个决定结果dtype的 kind。结果dtype的精度要尽可能低,但不能出现在图中任一输入dtype的左侧。
以及我们在将一个整型值与其他类型合并时需要选择带黄色边框的字段这一事实(请参见图右上角的文本),我们可以预期以下操作的输出类型为 np.complex128。我对它们进行了测试,结果正确。
complex(1+1j)+np.int8(1.0) # = np.complex128(2+1j)
complex(1+1j)+np.int16(1.0) # = np.complex128(2+1j)
complex(1+1j)+np.int32(1.0) # = np.complex128(2+1j)
complex(1+1j)+np.int64(1.0) # = np.complex128(2+1j)
对于下列这些操作,我们可以预期结果的类型为 np.complex64(现在我们处在虚线之上,因此不需要再坚持使用带黄边框的字段)。再次,预期得到的结果也符合。
complex(1+1j)+np.float16(1.0) # = np.complex64(2+1j)
complex(1+1j)+np.float32(1.0) # = np.complex64(2+1j)
然而,我不清楚为什么
complex(1+1j)+np.float64(1.0) # returns (2+1j), ie a Python scalar, not a np.complex128 value
返回 (2+1j),类型为 complex。complex 是一个Python标量,而不是numpy的 dtype。基于该图,我本来预期结果的类型为 np.complex128。
为什么将一个复数与一个 np.float64 值相加得到的是复数,而不是一个 np.complex128 值?
我现在能想到的合理解释有三点:1) 我在做错事,即没有测试我以为在测试的东西2) 我误解了该图,或者它比我现在意识到的还要有更多细微之处3) 该图是错的(极不可能)。
解决方案
这是NumPy的一个设计选择,numpy.float64 会继承自Python标准的“float”,因此它们可以直接兼容。
这意味着在所有右侧是numpy float64的运算中,使用的是浮点数的常规左侧内部运算符,因此得到的是Python的原生标量,而不是numpy的类型。
看看这个:
In [14]: type(np.float32(0)).__mro__
Out[14]:
(numpy.float32,
numpy.floating,
numpy.inexact,
numpy.number,
numpy.generic,
object)
In [15]: type(np.float64(0)).__mro__
Out[15]:
(numpy.float64,
numpy.floating,
numpy.inexact,
numpy.number,
numpy.generic,
float,
object)
Python的内部机制(这点有充分的文档记录)会在左侧操作数不知道如何处理该运算时,把运算委托给右侧的操作数(对它调用 __radd__,例如得到 +),但Python的原生复数“知道”如何处理浮点数,因此这样的运算从未委托给任何numpy的代码。
我猜想你应该注意到,只要交换操作数,就会得到numpy的类型:
In [16]: np.float64(1) + (1j)
Out[16]: np.complex128(1+1j)
