PAYATHON 2026

为什么 complex 与 np.float64 相加返回 complex 而非 np.complex128?

数据小陈

结论

在题述行为对应的 CPython 环境中,np.float64 继承 Python 内置的 float,所以左侧 complex 的加法实现可以直接接受它,并返回内置 complex。运算由 Python 完成,没有交给 NumPy 做类型提升。

NumPy 的类型提升图描述了它如何选择结果的 dtype。预测混合标量表达式时,还要先确认实际执行运算的是哪一方。

为什么 np.float64 会被 Python 直接处理

NumPy 标量的继承关系并不完全相同:np.float64 继承 Python float,而 np.float16、np.float32 不继承它;NumPy 整数类型也不继承 Python int。可以用 issubclass() 检查这些关系。NumPy 标量文档

import numpy as np

print(issubclass(np.float64, float))  # True
print(issubclass(np.float32, float))  # False
print(issubclass(np.float16, float))  # False
print(issubclass(np.int64, int))      # False

以 CPython 3.13 的实现为例,complex 加法接受内置复数、整数、浮点数及其子类。PyFloat_Check() 能识别 float 子类;计算完成后,PyComplex_FromCComplex() 创建内置 complex 对象。CPython 3.13 源码

题中表达式的运算过程可以这样理解:

  1. 左操作数是内置 complex,Python 先尝试它的加法实现。
  2. 右操作数 np.float64(1.0) 也是 float 实例,可以被该实现接受。
  3. 加法返回内置 complex,无需尝试 NumPy 的反向加法。

由于 np.float64 不是 complex 的子类,也不会触发右侧子类反向运算优先的规则。Python 对 __add__()、__radd__() 和 NotImplemented 的分派规则见数据模型文档。

为什么其他几组运算返回 NumPy 标量

对于 np.float16、np.float32 和题中的 NumPy 整数,内置 complex 加法无法通过上述内置类型路径直接处理,因而返回 NotImplemented。Python 随后尝试右操作数的反向加法,由 NumPy 接手计算。

运算进入 NumPy 后,才会应用相应的 dtype 提升规则。按照 NumPy 2.x 对 Python 标量的规则,Python complex 与 np.float16 或 np.float32 混合时可得到 np.complex64;与 NumPy 整数混合时则得到 np.complex128。这与题中的观察一致。NumPy 类型提升文档

这些表达式的运算分派路径不同。仅凭类型提升图,无法判断所有混合标量表达式最终返回的 Python 对象类型。

如何检查当前环境中的行为

原问题没有提供 Python 和 NumPy 版本。复现时应记录版本,并用 type() 检查,避免仅凭显示的 (2+1j) 判断类型:

import sys
import numpy as np

z = complex(1+1j)
x = np.float64(1.0)

print(sys.version)
print(np.__version__)

result = z + x
print(repr(result))
print(type(result))
print(type(result) is complex)
print(type(result) is np.complex128)

# 直接观察内置 complex 加法是否接受这些操作数。
print(complex.__add__(z, x))
print(complex.__add__(z, np.float32(1.0)))

在题述分派路径下,最后两行分别返回复数结果和 NotImplemented。直接调用这些特殊方法适合诊断,业务计算使用普通运算符或 NumPy API 即可。

如何明确获得 np.complex128

可以直接调用 NumPy 的 np.add(),并指定计算 dtype:

import numpy as np

z = complex(1+1j)
x = np.float64(1.0)

result = np.add(z, x, dtype=np.complex128)

print(result)
print(type(result))  # <class 'numpy.complex128'>

np.add() 是 NumPy 的 ufunc,支持 dtype 参数;两个输入都是标量时返回标量。numpy.add 文档

也可以先把复数显式转换为 NumPy 标量:

result = np.complex128(z) + x

若只想查看 NumPy 按提升规则选择的 dtype,可以使用:

print(np.result_type(z, x))  # complex128

np.result_type() 计算的是 NumPy 的提升结果,不会模拟 Python + 的完整运算符分派,因此不能保证它的结果与 type(z + x) 对应。numpy.result_type 文档

备注:内容仅供参考。