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
结构现在更加不透明,以允许我们添加额外的标志,并且项目大小不受 int
大小的限制,同时也允许未来改进结构化 dtypes,而不增加新 dtypes 的负担.
仅使用类型编号和其他初始字段的代码不受影响.希望大多数代码主要访问 ->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 和对齐.)
如果你使用这些新的访问器进行编译,并且需要兼容 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: 现有的用户 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_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``(以及相应的浮点和长双精度变体)来检索复数的实部或虚部,因为这些函数在 NumPy 1.x 和 NumPy 2.x 中都能工作.新增了函数 ``npy_csetreal
和 npy_csetimag
,以及兼容性宏 NPY_CSETREAL
和 ``NPY_CSETIMAG``(以及相应的浮点和长双精度变体),用于设置实部或虚部.
底层类型在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.
主命名空间#
大约100个主要的 np
命名空间成员已被弃用、移除或移动到新的位置.这样做是为了减少混乱并确立访问给定属性的唯一方式.下表显示了已被移除的成员:
移除的成员 |
迁移指南 |
---|---|
add_docstring |
它仍然可用作 |
add_newdoc |
它仍然可用作 |
add_newdoc_ufunc |
这是一个内部函数,没有替代品. |
alltrue |
使用 |
asfarray |
使用 |
byte_bounds |
现在它可以在 |
cast |
使用 |
cfloat |
使用 |
charrarray |
它仍然可用作 |
clongfloat |
使用 |
compare_chararrays |
它仍然可用作 |
compat |
没有替代方案,因为 Python 2 不再受支持. |
使用 |
|
cumproduct |
使用 |
数据源 |
它仍然可用作 |
弃用 |
使用 |
deprecate_with_doc |
使用 |
disp |
使用你自己的打印函数代替. |
fastCopyAndTranspose |
使用 |
find_common_type |
请使用 |
format_parser |
它仍然可用作 |
get_array_wrap |
|
float_ |
使用 |
geterrobj |
请使用 np.errstate 上下文管理器代替. |
Inf |
使用 |
无限 |
使用 |
infty |
使用 |
issctype |
使用 |
请使用 |
|
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 |
明确地访问数据类型. |
seterrobj |
请使用 np.errstate 上下文管理器代替. |
set_numeric_ops |
对于一般情况,使用 |
set_string_function |
使用 |
singlecomplex |
使用 |
使用 |
|
sometrue |
使用 |
source |
使用 |
tracemalloc_domain |
它现在可以从 |
unicode_ |
使用 |
谁 |
使用IDE变量浏览器或``locals()``代替. |
如果表格中没有包含您正在使用但在 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
命名空间已经被创建,其中大部分字符串操作作为 ufuncs 实现.旧的 numpy.char
命名空间仍然可用,并且在可能的情况下,使用新的 ufuncs 以获得更高的性能.我们推荐使用 strings
函数.`~numpy.char` 命名空间可能在将来被弃用.
其他更改#
关于序列化文件的注意事项#
NumPy 2.0 设计为可以加载使用 NumPy 1.26 创建的 pickle 文件,反之亦然.对于 1.25 及更早版本,加载 NumPy 2.0 的 pickle 文件将抛出异常.
适应 copy
关键字的变化#
在 asarray
、array
和 ndarray.__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=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发布期间非常重要.