- B站:啥都会一点的研究生
- 公众号:啥都会一点的研究生
如果你正在使用 Python 编写代码,那么很有可能正在直接或间接地使用 NumPy
如Pandas、Scikit-Image、SciPy、Scikit-Learn、AstroPy…这些都依赖于 NumPy
NumPy 2 是一个新的重要版本,候选版本将于 2024 年 2 月 1 日发布,最终版本将在一两个月后发布。重要的是,NumPy 2 向后不兼容,虽然不严重,但升级时可能需要做一些工作。这意味着需要确保在 NumPy 2 发布时你的应用程序不会崩溃
本文将介绍
- 新版本可能导致应用程序崩溃的不同方式
- 锁定软件包版本的重要性
- 如何确保应用程序在准备就绪之前不安装 NumPy 2
- 如何轻松升级代码以支持 NumPy 2
NumPy2 如何破坏应用程序
NumPy2可能对应用程序造成三种不同的影响,这是由新的不兼容依赖关系引起
- 代码:如果应用程序代码直接使用了NumPy的API,那么代码可能会出现问题
- 直接依赖项:代码中使用的库与NumPy 2不兼容
- 间接/传递性依赖项:代码中使用的库的依赖关系可能不兼容
修复代码可能相对容易,但直接或间接依赖的库不在你的掌控之下,通常由社区志愿者维护
以一个例子来说明,截至2024年1月9日,scikit-image
- 与NumPy2不兼容
- 在其打包元数据中声明与NumPy>=1.22兼容
当NumPy2发布时,可能所有现有版本都将声称与NumPy 2兼容,但实际上至少部分会出现问题。如果运气好的话,维护人员可能会在NumPy2发布时发布新的兼容版本,但这些都是志愿者,可能无法按时完成。而这只是众多库中的一个例子
锁定软件包版本
一旦NumPy 2发布,当它以新的依赖项安装时应用程序可能会出现问题
简而言之,有两种依赖项配置:
- 直接依赖项:在代码中直接导入的库的列表,即放在
pyproject.toml
或setup.py
中的依赖项 - 锁定文件:所有直接或间接的依赖项(依赖项的依赖项)列表,锁定到具体版本。如
requirements.txt
或其他依赖项工具使用的文件
在适当的时间间隔内,根据直接依赖项列表更新锁定文件
如何确保不安装NumPy 2
由于依赖项可能需要一些时间才能与NumPy 2兼容,因此可能希望坚持使用NumPy 1.x,这意味着要确保NumPy 2不会被安装。因此,无论是直接还是间接使用NumPy,都要确保依赖列表中的限制性依赖numpy<2
例如,如果使用 pyproject.toml
文件来配置 setuptools
# ...
[project]
dependencies = [
"pandas",
# For now, make sure NumPy 2 is not installed
"numpy<2",
]
如果使用的是 setup.py
,则
from setuptools import setup
setup(
# ...,
install_requires=[
"pandas",
# For now, make sure NumPy 2 is not installed
"numpy<2",
],
)
等待依赖库支持 NumPy 2
如果最终依赖的所有库都支持 NumPy 2。记住,不仅需要验证直接依赖库,还需验证间接依赖库:查看锁定文件中的库列表
升级代码和依赖库
首先,从依赖关系中移除第 1 步中添加的 numpy<2 限制,因为不再需要
其次,如果直接使用 NumPy,需要更新一些代码用法,详见 NumPy 2 移植指南
https://numpy.org/devdocs/numpy_2_0_migration_guide.html
使用 Ruff 升级代码
迁移指南解释说可以使用 Ruff 连接器自动升级代码以支持 NumPy 2。如果还没有使用 Ruff,或许应该试试:它是 Flake8、PyLint 和许多其他工具的更快替代品。要安装它,请使用
pip install ruff
或
conda install conda-forge::ruff
假设有以下example.py
代码
import numpy as np
arr1 = np.array([1 + 3j, 2], dtype=np.cfloat)
arr2 = np.array([2.0, 3.0], dtype=np.float_)
我们可以使用 ruff 查找与 NumPy 2 不兼容的地方
$ ruff check --preview --select NPY201 example.py
example.py:3:36: NPY201 [*] `np.cfloat` will be removed in NumPy 2.0. Use `numpy.complex128` instead.
example.py:4:35: NPY201 [*] `np.float_` will be removed in NumPy 2.0. Use `numpy.float64` instead.
Found 2 errors.
[*] 2 fixable with the `--fix` option.
之所以需要使用 --preview 选项,是因为该功能在 ruff 中仍不稳定。当准备好迁移时,也就是几个月后,这条 lint 规则将有望趋于稳定
可以添加 --fix
标志让 Ruff 为我们解决问题
$ ruff check --preview --fix --select NPY201 example.py
Found 2 errors (2 fixed, 0 remaining).
现在 example.py
长这样
import numpy as np
arr1 = np.array([1 + 3j, 2], dtype=np.complex128)
arr2 = np.array([2.0, 3.0], dtype=np.float64)
做好准备
NumPy 向后兼容 1.x 版本已经有一段时间了,但所有库最终都会发生向后不兼容的变化。因此应该
- 通过 pip-tools、pipenv、poes 或 conda-lock 等工具,锁定所有直接或间接(“传递”)的依赖关系
- 对于使用语义版本控制的库,即主要版本在不兼容的变更中发生变化的库,可以考虑添加抢先版本限制,如 numpy<2
- 确保定期更新依赖关系
https://pythonspeed.com/articles/numpy-2/