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 更改#

由于过时或无法维护,一些定义已被删除或替换。一些新的 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 大小的限制,以及允许在将来改进结构化 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 和对齐。)

对于用 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:现有的用户 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

警告

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

维度最大数量增加#

维度的最大数量(和参数)已增加到 64。这会影响 NPY_MAXDIMSNPY_MAXARGS 宏。可能需要检查它们的使用,并且我们通常鼓励您不要使用这些宏(特别是 NPY_MAXARGS),以便 NumPy 的未来版本可以删除此对维度数量的限制。

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

复数类型 - 底层类型更改#

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

建议使用函数 npy_crealnpy_cimag(以及相应的 float 和 long double 变体)来检索复数的实部或虚部,因为它们将适用于 NumPy 1.x 和 NumPy 2.x。添加了新的函数 npy_csetrealnpy_csetimag,以及兼容性宏 NPY_CSETREALNPY_CSETIMAG(以及相应的 float 和 long double 变体),用于设置实部或虚部。

底层类型在 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 — NumPy 2.0 的 Python API 清理

主命名空间#

np 命名空间的大约 100 个成员已弃用、移除或移至新位置。这样做是为了减少混乱,并建立只有一种方法来访问给定属性。下表显示了已被移除的成员

移除的成员

迁移指南

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

显式访问 dtype 代替。

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_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 命名空间,其中大多数字符串操作都以 ufunc 的形式实现。旧的 numpy.char 命名空间仍然可用,并且只要可能,就会使用新的 ufunc 来提高性能。我们建议以后使用 strings 函数。将来可能会弃用 char 命名空间。

其他更改#

关于腌制文件的说明#

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

适应 copy 关键字的更改#

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 发布期间很重要。