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并转换用户输入或同时支持longintp(以更好地支持NumPy 1.x)。在C或Cython中创建新的整数数组时,新的NPY_DEFAULT_INT宏将根据NumPy版本计算为NPY_LONGNPY_INTP

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

C-API 更改#

由于过时或无法维护,一些定义已被删除或替换。在 NumPy 2.0 和 NumPy 1.x 之间,某些新的 API 定义在运行时将产生不同的结果。有些定义在 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 上实现不同的行为。(compat 头文件以与这种用法兼容的方式定义它。)

如果您需要其他解决方法,请告知我们。

PyArray_Descr 结构已更改#

最具影响力的 C-API 更改之一是 PyArray_Descr 结构现在更加不透明,以便我们可以添加其他标志,并且项大小不受 int 大小的限制,还可以允许将来改进结构化 dtype,并且不会使新的 dtype 承担其字段的负担。

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

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

  • PyDataType_ELSIZEPyDataType_SET_ELSIZE(请注意,结果现在是 npy_intp 而不是 int)。

  • PyDataType_ALIGNMENT

  • PyDataType_FIELDSPyDataType_NAMESPyDataType_SUBARRAY

  • PyDataType_C_METADATA

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

如果同时使用 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 问题。

自定义用户 DType:现有用户 dtype 现在必须使用 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

警告

使用 npy_2_compat.h 头文件时,务必使用 import_array() 机制来确保可以访问完整的 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_MAXDIMSNPY_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_CSETREALNPY_CSETIMAG(以及相应的浮点数和长双精度浮点数变体),用于设置实部或虚部。

在 C++ 下,底层类型仍然是结构体(上述所有内容仍然有效)。

这对 Cython 有一定的影响。建议始终使用原生类型定义 cfloat_tcdouble_tclongdouble_t,而不是 NumPy 类型 npy_cfloat 等,除非您必须与使用 NumPy 类型的 C 代码进行交互。您仍然可以使用 c.realc.imag 属性(使用原生类型定义)编写 Cython 代码,但您不能再在 Cython 的 c++ 模式下使用就地运算符 c.imag += 1

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

要使用名称 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 代替。

DataSource

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

deprecate

直接使用 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 代替。

Infinity

使用 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

直接访问 dtypes 代替。

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_ 代替。

who

使用 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_utils, format, introspect, mixins, npyiostride_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 命名空间,其中大多数字符串操作都作为 ufunc 实现。旧的 numpy.char 命名空间仍然可用,并且在可能的情况下,使用新的 ufunc 以获得更高的性能。我们建议将来使用 strings 函数。 char 命名空间可能会在将来被弃用。

其他更改#

关于pickle文件的说明#

NumPy 2.0 旨在加载使用 NumPy 1.26 创建的 pickle 文件,反之亦然。对于 1.25 及更早版本,加载 NumPy 2.0 pickle 文件将引发异常。

适应`copy`关键字的更改#

copy 关键字行为更改asarrayarrayndarray.__array__ 中可能需要这些更改。

  • 使用 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 发行期间很重要。