NumPy 2.0 迁移指南#

本文档包含一组关于如何更新您的代码以与 NumPy 2.0 兼容的说明.它涵盖了 NumPy 的 Python 和 C API 的变化.

备注

请注意,NumPy 2.0 也打破了二进制兼容性 - 如果你正在为一个依赖于 NumPy 的 C API 的 Python 包分发二进制文件,请参阅 NumPy 2.0 特定建议.

Ruff 插件#

2.0 版本说明和本迁移指南中涵盖的许多更改可以通过专用 Ruff 规则在下游代码中自动适应,即规则 NPY201.

你应该安装 ruff>=0.4.8 并将 NPY201 规则添加到你的 pyproject.toml 中:

[tool.ruff.lint]
select = ["NPY201"]

你也可以从命令行直接应用 NumPy 2.0 规则:

$ ruff check path/to/code/ --select NPY201

对 NumPy 数据类型提升的更改#

NumPy 2.0 根据 NEP 50 更改了类型提升(合并不同数据类型的结果).请参阅 NEP 以了解此更改的详细信息.它包括一个示例更改表和一个向后兼容性部分.

最大的向后兼容性变化是标量的精度现在被一致地保留了.两个例子是:

  • np.float32(3) + 3. 现在返回一个 float32,而之前返回的是一个 float64.

  • np.array([3], dtype=np.float32) + np.float64(3) 现在将返回一个 float64 数组.(标量的更高精度不会被忽略.)

对于浮点值,这可能导致在使用标量时精度降低.对于整数,可能会出现错误或溢出.

要解决这个问题,你可能需要显式地进行类型转换.通常,通过 int()float()numpy_scalar.item() 确保你使用的是 Python 标量也可能是一个好的解决方案.

要跟踪更改,您可以启用对更改行为的警告(使用 warnings.simplefilter 将其作为错误引发以获取回溯):

np._set_promotion_state("weak_and_warn")

这在测试期间很有用.不幸的是,运行这可能会标记许多在实践中不相关的更改.

Windows 默认整数#

NumPy 现在在所有 64 位系统上使用的默认整数是 64 位(在 32 位系统上是 32 位).由于与 Python 2 相关的历史原因,它之前等同于 C 的 long 类型.现在默认整数等同于 np.intp.

大多数终端用户不应受到此更改的影响.某些操作将使用更多内存,但某些操作实际上可能会变得更快.如果您由于调用用编译语言编写的库而遇到问题,显式转换为 long 可能会有所帮助,例如: arr = arr.astype("long", copy=False).

用C、Cython或类似语言编写的与编译代码交互的库可能需要更新以适应用户输入,如果它们在C端使用``long``或等效类型.在这种情况下,你可能希望使用``intp``并转换用户输入或同时支持``long``和``intp``(以更好地支持NumPy 1.x).在C或Cython中创建新的整数数组时,新的``NPY_DEFAULT_INT``宏将根据NumPy版本评估为``NPY_LONG``或``NPY_INTP``.

请注意,NumPy 随机 API 不受此更改影响.

C-API 变更#

由于过时或难以维护,一些定义已被移除或替换.一些新的 API 定义在 NumPy 2.0 和 NumPy 1.x 之间的运行时会有不同的评估.一些定义在 numpy/_core/include/numpy/npy_2_compat.h 中(例如 NPY_DEFAULT_INT),可以全部或部分引入,以便在编译时针对 NumPy 1.x 时可以使用这些定义.

如有必要,可以使用 PyArray_RUNTIME_VERSION >= NPY_2_0_API_VERSION 在 NumPy 1.x 和 2.0 上显式实现不同的行为.(兼容头文件以兼容这种方式定义它.)

请告知我们是否需要在此处提供额外的解决方案.

PyArray_Descr 结构体已更改#

最具影响力的 C-API 变化之一是 PyArray_Descr 结构现在更加不透明,以允许我们添加额外的标志,并且项目大小不受 int 大小的限制,同时也允许未来改进结构化 dtypes,而不增加新 dtypes 的负担.

仅使用类型编号和其他初始字段的代码不受影响.希望大多数代码主要访问 ->elsize 字段,当 dtype/描述符本身附加到数组时(例如 arr->descr->elsize),这最好替换为 PyArray_ITEMSIZE(arr).

在不可能的情况下,需要新的访问函数:

  • PyDataType_ELSIZEPyDataType_SET_ELSIZE (注意结果现在是 npy_intp 而不是 int).

  • PyDataType_ALIGNMENT

  • PyDataType_FIELDS, PyDataType_NAMES, PyDataType_SUBARRAY

  • PyDataType_C_METADATA

Cython 代码应使用 Cython 3,在这种情况下,更改是透明的.(当仅针对 NumPy 2 编译时,结构访问可用于 elsize 和对齐.)

如果你使用这些新的访问器进行编译,并且需要兼容 1.x 和 2.x 版本,不幸的是,有必要通过类似宏的方式在本地定义它们:

#if NPY_ABI_VERSION < 0x02000000
  #define PyDataType_ELSIZE(descr) ((descr)->elsize)
#endif

或者将 npy2_compat.h 添加到你的代码库中,并在使用 NumPy 1.x 编译时显式包含它(因为它们是新的 API).包含该文件对 NumPy 2 没有影响.

如果您需要帮助或提供的功能不足,请不要犹豫打开一个 NumPy 问题.

自定义用户 DTypes: 现有的用户 dtypes 现在必须使用 PyArray_DescrProto 来定义它们的 dtype 并对代码进行轻微修改.请参阅 PyArray_RegisterDataType 中的注释.

功能已移至需要 import_array() 的头部#

如果你之前只包含了 ndarraytypes.h,你可能会发现一些功能不再可用,需要包含 ndarrayobject.h 或类似的文件.当将 npy_2_compat.h 引入到你自己的代码库中以允许在使用 NumPy 1.x 编译时使用新定义时,也需要这个包含.

以前不需要导入的功能包括:

  • 访问 dtype 标志的函数:PyDataType_FLAGCHKPyDataType_REFCHK 以及相关的 NPY_BEGIN_THREADS_DESCR.

  • PyArray_GETITEMPyArray_SETITEM.

警告

重要的是,使用 import_array() 机制以确保在使用 npy_2_compat.h 头文件时可以访问完整的 NumPy API.在大多数情况下,您的扩展模块可能已经调用了它.然而,如果没有,我们已经添加了 PyArray_ImportNumPyAPI() 作为一种更可取的方式来确保导入 NumPy API.这个函数在多次调用时是轻量级的,因此您可以在任何需要的地方插入它(如果您希望避免在模块导入时设置它).

增加最大维度数#

最大维度数(和参数数)增加到64.这影响了 NPY_MAXDIMSNPY_MAXARGS 宏.审查它们的使用可能是个好主意,我们通常鼓励您不要使用这些宏(尤其是 NPY_MAXARGS),以便未来版本的 NumPy 可以移除对维度数的这一限制.

NPY_MAXDIMS 也被用于在 C-API 中表示 axis=None,包括 PyArray_AxisConverter.后者将返回 -2147483648 作为轴(最小的整数值).其他函数可能会报错 AxisError: axis 64 is out of bounds for array of dimension,在这种情况下,你需要传递 NPY_RAVEL_AXIS 而不是 NPY_MAXDIMS.``NPY_RAVEL_AXIS`` 在 npy_2_compat.h 头文件中定义,并且运行时依赖(在 NumPy 1.x 中映射到 32,在 NumPy 2.x 中映射到 -2147483648).

复杂类型 - 底层类型变化#

所有复杂类型的底层C类型已更改为使用原生C99类型.虽然这些类型的内存布局与NumPy 1.x中使用的类型相同,但API略有不同,因为不再可能直接访问字段(如 c.realc.imag).

建议使用函数 npy_crealnpy_cimag``(以及相应的浮点和长双精度变体)来检索复数的实部或虚部,因为这些函数在 NumPy 1.x NumPy 2.x 中都能工作.新增了函数 ``npy_csetrealnpy_csetimag,以及兼容性宏 NPY_CSETREAL``NPY_CSETIMAG``(以及相应的浮点和长双精度变体),用于设置实部或虚部.

底层类型在C++中仍然是一个结构体(上述所有内容仍然有效).

这对Cython有影响.建议始终使用本地的typedef cfloat_t, cdouble_t, clongdouble_t 而不是NumPy类型 npy_cfloat 等,除非你必须与使用NumPy类型的C代码进行接口.你仍然可以使用 c.realc.imag 属性(使用本地typedef)编写cython代码,但在Cython的c++模式下,你不能再使用就地运算符 c.imag += 1.

因为 NumPy 2 现在包含了使用名为 I 变量的 complex.h 代码,可能会看到如下错误:

使用名称 I 现在需要 #undef I.

备注

NumPy 2.0.1 短暂地包含了 #undef I 以帮助尚未包含 complex.h 的用户.

命名空间的更改#

在 NumPy 2.0 中,某些函数、模块和常量被移动或删除,以通过删除不必要的或过时的功能并明确 NumPy 的哪些部分被认为是私有的,使 NumPy 命名空间更加用户友好.请参阅下表以获取迁移指导.对于大多数更改,这意味着将其替换为向后兼容的替代方案.

更多详情请参阅 NEP 52 — Python API cleanup for NumPy 2.0.

主命名空间#

大约100个主要的 np 命名空间成员已被弃用、移除或移动到新的位置.这样做是为了减少混乱并确立访问给定属性的唯一方式.下表显示了已被移除的成员:

移除的成员

迁移指南

add_docstring

它仍然可用作 np.lib.add_docstring.

add_newdoc

它仍然可用作 np.lib.add_newdoc.

add_newdoc_ufunc

这是一个内部函数,没有替代品.

alltrue

使用 np.all 代替.

asfarray

使用 np.asarray 并指定浮点数数据类型.

byte_bounds

现在它可以在 np.lib.array_utils.byte_bounds 下使用

cast

使用 np.asarray(arr, dtype=dtype) 代替.

cfloat

使用 np.complex128 代替.

charrarray

它仍然可用作 np.char.chararray.

clongfloat

使用 np.clongdouble 代替.

compare_chararrays

它仍然可用作 np.char.compare_chararrays.

compat

没有替代方案,因为 Python 2 不再受支持.

complex_

使用 np.complex128 代替.

cumproduct

使用 np.cumprod 代替.

数据源

它仍然可用作 np.lib.npyio.DataSource.

弃用

使用 warnings.warn 直接发出 DeprecationWarning ,或者使用 typing.deprecated.

deprecate_with_doc

使用 warnings.warn 直接发出 DeprecationWarning ,或者使用 typing.deprecated.

disp

使用你自己的打印函数代替.

fastCopyAndTranspose

使用 arr.T.copy() 代替.

find_common_type

请使用 numpy.promote_typesnumpy.result_type 代替.要实现 scalar_types 参数的语义,请使用 numpy.result_type 并传递 Python 值 00.00j.

format_parser

它仍然可用作 np.rec.format_parser.

get_array_wrap

float_

使用 np.float64 代替.

geterrobj

请使用 np.errstate 上下文管理器代替.

Inf

使用 np.inf 代替.

无限

使用 np.inf 代替.

infty

使用 np.inf 代替.

issctype

使用 issubclass(rep, np.generic) 代替.

issubclass_

请使用 issubclass 内置函数代替.

issubsctype

使用 np.issubdtype 代替.

mat

使用 np.asmatrix 代替.

maximum_sctype

使用特定的 dtype 代替.你应该避免依赖任何隐式机制,并在代码中显式选择某种类型的最大 dtype.

NaN

使用 np.nan 代替.

nbytes

使用 np.dtype(<dtype>).itemsize 代替.

NINF

使用 -np.inf 代替.

NZERO

使用 -0.0 代替.

longcomplex

使用 np.clongdouble 代替.

longfloat

使用 np.longdouble 代替.

lookfor

直接搜索 NumPy 的文档.

obj2sctype

请使用 np.dtype(obj).type 代替.

PINF

使用 np.inf 代替.

product

使用 np.prod 代替.

PZERO

使用 0.0 代替.

recfromcsv

使用 np.genfromtxt 并设置逗号为分隔符.

recfromtxt

使用 np.genfromtxt 代替.

round_

使用 np.round 代替.

safe_eval

使用 ast.literal_eval 代替.

sctype2char

请使用 np.dtype(obj).char 代替.

sctypes

明确地访问数据类型.

seterrobj

请使用 np.errstate 上下文管理器代替.

set_numeric_ops

对于一般情况,使用 PyUFunc_ReplaceLoopBySignature.对于 ndarray 子类,定义 __array_ufunc__ 方法并覆盖相关的 ufunc.

set_string_function

使用 np.set_printoptions 并使用格式化器来自定义打印 NumPy 对象.

singlecomplex

使用 np.complex64 代替.

string_

使用 np.bytes_ 代替.

sometrue

使用 np.any 代替.

source

使用 inspect.getsource 代替.

tracemalloc_domain

它现在可以从 np.lib 中获得.

unicode_

使用 np.str_ 代替.

使用IDE变量浏览器或``locals()``代替.

如果表格中没有包含您正在使用但在 2.0 中被移除的项目,那么这意味着它是一个私有成员.您应该使用现有的 API,或者在不可行的情况下,联系我们请求恢复被移除的条目.

下表列出了已弃用的成员,这些成员将在 2.0 之后的版本中移除:

已弃用的成员

迁移指南

in1d

使用 np.isin 代替.

row_stack

使用 np.vstack 代替(row_stackvstack 的别名).

trapz

使用 np.trapezoidscipy.integrate 函数代替.

最后,一组内部枚举已被移除.由于它们在下游库中未被使用,我们不提供任何关于如何替换它们的信息:

[FLOATING_POINT_SUPPORT, FPE_DIVIDEBYZERO, FPE_INVALID, FPE_OVERFLOW, FPE_UNDERFLOW, UFUNC_BUFSIZE_DEFAULT, UFUNC_PYVALS_NAME, CLIP, WRAP, RAISE, BUFSIZE, ALLOW_THREADS, MAXDIMS, MAY_SHARE_EXACT, MAY_SHARE_BOUNDS]

numpy.lib 命名空间#

np.lib 中可用的大多数函数也存在于主命名空间中,这是它们的主要位置.为了明确如何访问每个公共函数,``np.lib`` 现在是空的,只包含少数几个专门的子模块、类和函数:

  • array_utilsformatintrospectmixinsnpyiostride_tricks 子模块,

  • ArrayteratorNumpyVersion 类,

  • add_docstringadd_newdoc 函数

  • tracemalloc_domain 常量.

如果在访问 np.lib 的属性时遇到 AttributeError ,你应该尝试从主 np 命名空间访问它.如果某个项目在主命名空间中也缺失,那么你使用的是一个私有成员.你应该使用现有的 API,或者在不可行的情况下,联系我们请求恢复被移除的条目.

numpy.core 命名空间#

np.core 命名空间现在正式成为私有,并已重命名为 np._core.用户不应直接从 _core 获取成员 - 相反,应使用主命名空间来访问相关属性.``_core`` 模块的布局可能会在未来无通知的情况下更改,这与遵循弃用期政策的公共模块相反.如果主命名空间中也缺少某个项目,那么您应该使用现有的API,或者在不可行的情况下,联系我们请求恢复已移除的条目.

ndarray 和 标量 方法#

一些来自 np.ndarraynp.generic 标量类的方法已被移除.下表提供了已移除成员的替代方案:

过期成员

迁移指南

newbyteorder

使用 arr.view(arr.dtype.newbyteorder(order)) 代替.

ptp

使用 np.ptp(arr, ...) 代替.

setitem

使用 arr[index] = value 代替.

numpy.strings 命名空间#

一个新的 numpy.strings 命名空间已经被创建,其中大部分字符串操作作为 ufuncs 实现.旧的 numpy.char 命名空间仍然可用,并且在可能的情况下,使用新的 ufuncs 以获得更高的性能.我们推荐使用 strings 函数.`~numpy.char` 命名空间可能在将来被弃用.

其他更改#

关于序列化文件的注意事项#

NumPy 2.0 设计为可以加载使用 NumPy 1.26 创建的 pickle 文件,反之亦然.对于 1.25 及更早版本,加载 NumPy 2.0 的 pickle 文件将抛出异常.

适应 copy 关键字的变化#

asarrayarrayndarray.__array__copy 关键字行为变化 可能需要这些更改:

  • 使用 np.array(..., copy=False) 的代码在大多数情况下可以改为 np.asarray(...).旧代码倾向于这样使用 np.array,因为它比默认的 np.asarray 复制所需行为的开销更小.这已不再正确,``np.asarray`` 是首选函数.

  • 对于需要显式传递 None/False 表示”如果需要则复制”的代码,这种方式兼容 NumPy 1.x 和 2.x,请参见 scipy#20172 以了解如何实现.

  • 对于非NumPy数组类对象上的任何 __array__ 方法,必须在签名中添加 dtype=Nonecopy=None 关键字 - 这将兼容旧版本的NumPy(尽管旧版本的numpy永远不会传递 copy 关键字).如果将关键字添加到 __array__ 签名中,那么对于:

    • copy=True 和任何 dtype 值总是返回一个新的副本.

    • copy=None 如果需要(例如通过 dtype),则创建一个副本.

    • copy=False 绝不能进行复制.如果需要复制以返回一个 numpy 数组或满足 dtype,则引发异常 (ValueError).

编写依赖于 numpy 版本的代码#

显式地根据 numpy 版本编写代码的情况应该相当少见 - 在大多数情况下,代码可以重写以同时兼容 1.x 和 2.0.然而,如果必要的话,这里有一个建议的代码模式,使用 numpy.lib.NumpyVersion:

# example with AxisError, which is no longer available in
# the main namespace in 2.0, and not available in the
# `exceptions` namespace in <1.25.0 (example uses <2.0.0b1
# for illustrative purposes):
if np.lib.NumpyVersion(np.__version__) >= '2.0.0b1':
    from numpy.exceptions import AxisError
else:
    from numpy import AxisError

这种模式将正确工作,包括在NumPy发布候选版本中,这在2.0.0发布期间非常重要.