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 更改#
由于过时或无法维护,一些定义已被删除或替换。在 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_ELSIZE
和PyDataType_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 问题。
自定义用户 DType:现有用户 dtype 现在必须使用 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_GETITEM
和PyArray_SETITEM
。
警告
使用 npy_2_compat.h
头文件时,务必使用 import_array()
机制来确保可以访问完整的 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
(以及相应的浮点数和长双精度浮点数变体)来获取复数的实部或虚部,因为这些函数可以与 NumPy 1.x 和 NumPy 2.x 兼容。新增了函数 npy_csetreal
和 npy_csetimag
,以及兼容性宏 NPY_CSETREAL
和 NPY_CSETIMAG
(以及相应的浮点数和长双精度浮点数变体),用于设置实部或虚部。
在 C++ 下,底层类型仍然是结构体(上述所有内容仍然有效)。
这对 Cython 有一定的影响。建议始终使用原生类型定义 cfloat_t
、cdouble_t
、clongdouble_t
,而不是 NumPy 类型 npy_cfloat
等,除非您必须与使用 NumPy 类型的 C 代码进行交互。您仍然可以使用 c.real
和 c.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 |
它仍然可用,作为 |
add_newdoc |
它仍然可用,作为 |
add_newdoc_ufunc |
这是一个内部函数,没有替代方案。 |
alltrue |
使用 |
asfarray |
使用带浮点类型的 |
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
和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`关键字的更改#
copy 关键字行为更改 在 asarray
、 array
和 ndarray.__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=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 发行期间很重要。