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 结构体现在更加不透明,以便我们可以添加额外的标志,并且让itemsizes不受 int 大小的限制,以及允许将来改进结构化dtype,并且不会让新的dtype承受其字段的负担.
仅使用类型编号和其他初始字段的代码不会受到影响. 大多数代码可能主要访问 ->elsize 字段,当dtype/描述符本身附加到数组时 (例如 arr->descr->elsize ) ,最好将其替换为 PyArray_ITEMSIZE(arr) .
如果不可能,则需要新的访问器函数:
PyDataType_ELSIZE和PyDataType_SET_ELSIZE(请注意,结果现在是npy_intp而不是int).PyDataType_ALIGNMENTPyDataType_FIELDS,PyDataType_NAMES,PyDataType_SUBARRAYPyDataType_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_GETITEM和PyArray_SETITEM.
警告
重要的是,使用 import_array() 机制以确保在使用 npy_2_compat.h 头文件时可以访问完整的 NumPy API. 在大多数情况下,您的扩展模块可能已经调用了它. 但是,如果没有,我们添加了 PyArray_ImportNumPyAPI() 作为确保导入 NumPy API 的首选方法. 此函数在多次调用时是轻量级的,因此您可以将其插入到任何可能需要的地方(如果您希望避免在模块导入时设置它).
增加最大维度数#
最大维度数(和参数)增加到64. 这影响了 NPY_MAXDIMS 和 NPY_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.real 或 c.imag ).
建议使用函数 npy_creal 和 npy_cimag (以及相应的 float 和 long double 变体)来检索复数的实部或虚部,因为这些函数既适用于 NumPy 1.x,也适用于 NumPy 2.x.新增了函数 npy_csetreal 和 npy_csetimag ,以及兼容性宏 NPY_CSETREAL 和 NPY_CSETIMAG (以及相应的 float 和 long double 变体),用于设置实部或虚部.
在 C++ 下,底层类型仍然是一个结构体(以上所有内容仍然有效).
这对 Cython 有影响.建议始终使用原生 typedef cfloat_t , cdouble_t , clongdouble_t ,而不是 NumPy 类型 npy_cfloat 等,除非必须与使用 NumPy 类型编写的 C 代码进行交互.您仍然可以使用 c.real 和 c.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 |
它仍然可以作为 |
add_newdoc |
它仍然可以作为 |
add_newdoc_ufunc |
它是一个内部函数,没有替代品. |
alltrue |
使用 |
asfarray |
使用带有 float dtype 的 |
byte_bounds |
现在它可以在 |
cast |
使用 |
cfloat |
使用 |
charrarray |
它仍然可以作为 |
clongfloat |
使用 |
compare_chararrays |
它仍然可以作为 |
compat |
没有替代品,因为不再支持 Python 2. |
complex_ |
使用 |
cumproduct |
请使用 |
DataSource |
它仍然可以作为 |
deprecate |
直接使用 |
deprecate_with_doc |
直接使用 |
disp |
请使用您自己的打印函数代替. |
fastCopyAndTranspose |
请使用 |
find_common_type |
请使用 |
format_parser |
它仍然可以作为 |
get_array_wrap |
|
float_ |
请使用 |
geterrobj |
请改用 np.errstate 上下文管理器. |
Inf |
请使用 |
Infinity |
请使用 |
infty |
请使用 |
issctype |
请使用 |
issubclass_ |
请使用内置的 |
issubsctype |
请使用 |
mat |
请使用 |
maximum_sctype |
请使用特定的 dtype 代替.您应该避免依赖任何隐式机制,并在代码中显式选择某种类型的最大 dtype. |
NaN |
请使用 |
nbytes |
请使用 |
NINF |
请使用 |
NZERO |
请使用 |
longcomplex |
使用 |
longfloat |
请使用 |
lookfor |
直接搜索 NumPy 的文档. |
obj2sctype |
请使用 |
PINF |
请使用 |
product |
请使用 |
PZERO |
请使用 |
recfromcsv |
请使用带逗号分隔符的 |
recfromtxt |
请使用 |
round_ |
请使用 |
safe_eval |
请使用 |
sctype2char |
请使用 |
sctypes |
请显式访问 dtypes 代替. |
seterrobj |
请改用 np.errstate 上下文管理器. |
set_numeric_ops |
对于一般情况,请使用 |
set_string_function |
请使用 |
singlecomplex |
请使用 |
string_ |
使用 |
sometrue |
使用 |
source |
使用 |
tracemalloc_domain |
现在可以从 |
unicode_ |
使用 |
who |
使用 IDE 变量浏览器或 |
如果表格中不包含您曾经使用但在 2.0 中删除的项目,则表示它是一个私有成员. 您应该使用现有的 API,或者在不可行的情况下,联系我们并请求恢复删除的条目.
下表列出了已弃用的成员,它们将在 2.0 之后的版本中删除:
已弃用的成员 |
迁移指南 |
|---|---|
in1d |
使用 |
row_stack |
使用 |
trapz |
使用 |
最后,一组内部枚举已被删除. 由于下游库中没有使用它们,因此我们不提供任何有关如何替换它们的信息:
[ 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,scimath和stride_tricks子模块,Arrayterator和NumpyVersion类,add_docstring和add_newdoc函数,tracemalloc_domain常量.
如果在从 np.lib 访问属性时收到 AttributeError ,则应尝试从主 np 命名空间访问它. 如果主命名空间中也缺少某个项目,那么您正在使用私有成员. 您应该使用现有的 API,或者在不可行的情况下,联系我们并请求恢复删除的条目.
numpy.core 命名空间#
np.core 命名空间现在是正式的私有命名空间,并且已重命名为 np._core . 用户永远不应直接从 _core 中获取成员 - 相反,应使用主命名空间来访问有问题的属性. _core 模块的布局将来可能会更改,恕不另行通知,这与遵守弃用期策略的公共模块相反. 如果主命名空间中也缺少某个项目,那么您应该使用现有的 API,或者在不可行的情况下,联系我们并请求恢复删除的条目.
ndarray 和标量方法#
np.ndarray 和 np.generic 标量类中的一些方法已被删除. 下表提供了已删除成员的替换:
过期的成员 |
迁移指南 |
|---|---|
newbyteorder |
使用 |
ptp |
使用 |
setitem |
使用 |
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 , array 和 ndarray.__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=None和copy=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 发布期间非常重要.