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 .

大多数最终用户不会受到此更改的影响.某些操作将使用更多内存,但某些操作实际上可能会变得更快.如果您由于调用以编译语言(例如 C,Cython) 编写的库而遇到问题,则显式转换为 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 ),可以完整或部分地 vendor 以在针对 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/descriptor 本身附加到数组时 (例如 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 issue.

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

功能已移动到需要 import_array() 的头文件中#

如果您之前只包含 ndarraytypes.h ,您可能会发现某些功能不再可用,并且需要包含 ndarrayobject.h 或类似的文件. 当将 npy_2_compat.h 融入您自己的代码库以允许在使用 NumPy 1.x 编译时使用新的定义时,也需要包含此文件.

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

  • 访问 dtype 标志的函数: PyDataType_FLAGCHK , PyDataType_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_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 有影响. 建议始终使用原生 typedef cfloat_t , cdouble_t , clongdouble_t ,而不是 NumPy 类型 npy_cfloat 等,除非您必须与使用 NumPy 类型编写的 C 代码交互. 您仍然可以使用 c.realc.imag 属性(使用原生 typedef)编写 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 .

主命名空间#

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

已删除成员

迁移指南

add_docstring

它仍然可以作为 np.lib.add_docstring 使用.

add_newdoc

它仍然可以作为 np.lib.add_newdoc 使用.

add_newdoc_ufunc

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

alltrue

请改用 np.all .

asfarray

请改用带有 float dtype 的 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 值 0 , 0.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 , npyio , scimathstride_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 函数. char 命名空间将来可能会被弃用.

其他更改#

关于 pickle 文件的说明#

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

适应 copy 关键字的更改#

copy keyword behavior changesasarray , arrayndarray.__array__ 中可能需要以下更改:

  • 使用 np.array(..., copy=False) 的代码在大多数情况下可以更改为 np.asarray(...) .较旧的代码倾向于这样使用 np.array ,因为它比默认的 np.asarray copy-if-needed 行为开销更小.现在情况并非如此, 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 发布期间非常重要.