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 结构体现在更加不透明,以便我们可以添加额外的标志,并且让itemsizes不受 int 大小的限制,以及允许将来改进结构化dtype,并且不会让新的dtype承受其字段的负担.

仅使用类型编号和其他初始字段的代码不会受到影响. 大多数代码可能主要访问 ->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和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 issue.

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

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

如果您以前只包含 ndarraytypes.h ,您可能会发现某些功能不再可用,需要包含 ndarrayobject.h 或类似的文件.当将 npy_2_compat.h vendor到您自己的代码库中时,也需要包含该文件,以便在使用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 现在包含使用名为 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 了解更多详情.

主命名空间#

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

其他更改#

关于 pickle 文件的说明#

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

适应 copy 关键字的更改#

asarray , arrayndarray.__array__copy keyword behavior changes 可能需要以下更改:

  • 使用 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 发布期间非常重要.