NumPy 1.20.1 版本发布详解:快速修复 distutils 与 random.shuffle 回归问题
2026/9/20 17:11:53 网站建设 项目流程

NumPy 1.20.1 版本发布详解:快速修复 distutils 与 random.shuffle 回归问题

【免费下载链接】numpyThe fundamental package for scientific computing with Python.项目地址: https://gitcode.com/gh_mirrors/nu/numpy

NumPy 1.20.1 是 1.20 系列的首个快速修复(bugfix)版本,专门用于修复 1.20.0 发布后社区报告的一系列缺陷与回归问题。本篇以官方发布说明(doc/source/release/1.20.1-notes.rst)为主体,结合本仓库源码与测试用例,逐项剖析本次修复的根因、实现细节与验证方式,帮助读者了解 NumPy 版本发布流程中 bugfix 分支的运作机制,以及这些修复对下游用户的实际影响。

版本定位:面向回归的快速修复版

NumPy 1.20.1 不是一个功能版本,而是紧跟在 1.20.0 之后的快速 bugfix 版本(rapid bugfix release)。官方说明明确指出,该版本的目标是修复 1.20.0 发布后报告的几个 bug 与回归问题(regressions):

  • 修复了导致下游项目出问题的 distutils bug;
  • 修复了random.shuffle的回归问题。

本次发布共合并 15 个 pull request,共有 8 位贡献者参与,其中 Nicholas McKibben 与 @Aerysv 为首次贡献补丁的新人(名字旁带 "+" 标记)。从 PR 的构成看,本次修复横跨构建系统(distutils、threads.h探测、f2py)、随机数子系统(shuffle、C 链接)、核心函数(isclose/allclose与 timedelta)以及类型注解与 CI 基础设施等多个方向,下面逐一展开。

Highlights 一:distutils 构建问题修复

问题背景:迭代中修改 fake libraries 列表

1.20.0 引入的 distutils 缺陷会破坏下游项目的构建。修复该问题的 PR 标题为"BUG: don't mutate list of fake libraries while iterating over..."(PR #18326),其核心是:distutils 在解析依赖时维护一个"伪造库"(fake libraries)列表,而原实现在遍历该列表的过程中直接对其进行了修改,这违反了"迭代时禁止修改容器"的基本约束,导致在特定链接场景下下游项目构建失败。

从修复类型(BUG)与后续 1.20.x 系列发布说明的稳定性可以看出,这类构建路径缺陷对使用 NumPy 构建链的第三方包影响最大。若下游项目恰好命中该遍历逻辑,升级到 1.20.0 后会出现链接错误,而 1.20.1 通过改为先收集、后修改的方式(从代码结构看,即遍历副本或延迟收集待处理项)恢复了正常的依赖解析流程。

相关构建修复:signed_charthreads.h探测

同属构建系统的还有两个修复:

  • PR #18351"BUG: Fix missing signed_char dependency. Closes #18335"):修复了signed_char依赖缺失的问题,该缺陷由 issue #18335 报告,会导致特定平台上的类型映射不完整;
  • PR #18357"BUG: threads.h existence test requires GLIBC > 2.12"):修正了threads.h存在性探测的逻辑。原探测条件过于宽松,未考虑 glibc 版本下限,修复后要求GLIBC > 2.12才判定threads.h可用,避免在较老 glibc 环境下误判导致编译期问题。

这两项修复共同保证了 1.20.1 在更多 Linux 发行版与交叉编译场景下的可构建性。

Highlights 二:random.shuffle回归修复

random.shuffle回归是本版本另一个核心亮点。官方说明将其单独列出,本仓库源码与测试为我们提供了完整的证据链。

回归一:无法优雅处理 memoryview(gh-18273)

在 numpy/random/tests/test_random.py 中可以看到专门为该回归添加的回归测试:

def test_shuffle_memoryview(self): # gh-18273 # allow graceful handling of memoryviews # (treat the same as arrays) rng = random.RandomState(self.seed) a = np.arange(5).data rng.shuffle(a) assert_equal(np.asarray(a), [0, 1, 4, 3, 2]) rng = random.RandomState(self.seed) rng.shuffle(a) assert_equal(np.asarray(a), [0, 1, 2, 3, 4]) rng = np.random.default_rng(self.seed) rng.shuffle(a) assert_equal(np.asarray(a), [4, 1, 0, 3, 2])

该测试同时覆盖了两套 API:

  • 传统 API:np.random.RandomState.shuffle
  • 新 API:np.random.default_rng().shuffle(即Generator.shuffle)。

测试断言对 memoryview(这里是np.arange(5).data,即ndarray的 buffer 内存视图)调用shuffle后,底层的数组内容被正确、可复现地打乱,且两次使用相同种子 shuffle 的结果相互还原。修复 PR #18327("MAINT: gracefully shuffle memoryviews")使得 memoryview 被"当作数组一样"优雅处理,而非抛出异常或产生未定义行为。

回归二:随机分布函数缺少 C 链接(PR #18328)

PR #18328("BUG: Use C linkage for random distributions")修复了随机分布函数缺失 C linkage 的问题。在 numpy/random/mtrand.pyx 中,shuffle的实现展现了其底层机制:

def shuffle(self, object x): ... if isinstance(x, np.ndarray) and not x.flags.writeable: raise ValueError('array is read-only') if type(x) is np.ndarray and x.ndim == 1 and x.size: # Fast, statically typed path: shuffle the underlying buffer. # Only for non-empty, 1d objects of class ndarray (subclasses such # as MaskedArrays may not support this approach). x_ptr = np.PyArray_BYTES(x) stride = x.strides[0] itemsize = x.dtype.itemsize # As the array x could contain python objects we use a buffer # of bytes for the swaps to avoid leaving one of the objects # within the buffer and erroneously decrementing it's refcount # when the function exits. buf = np.empty(itemsize, dtype=np.int8) # GC'd at function exit buf_ptr = np.PyArray_BYTES(buf) with self.lock: # We trick gcc into providing a specialized implementation for # the most common case, yielding a ~33% performance improvement. if itemsize == sizeof(np.npy_intp): self._shuffle_raw(n, sizeof(np.npy_intp), stride, x_ptr, buf_ptr) else: self._shuffle_raw(n, itemsize, stride, x_ptr, buf_ptr)

要点解析:

  1. 只读数组保护:对不可写(writeable=False)的数组直接抛出ValueError
  2. 一维 ndarray 快速路径:仅对"恰好是ndarray类型(而非子类)、一维、非空"的输入走静态类型化的底层 buffer 交换路径,因为 MaskedArray 等子类可能不支持该方案;
  3. 引用计数安全:交换使用int8字节缓冲,避免在交换过程中让 Python 对象残留在缓冲区内、函数退出时被错误地减少引用计数;
  4. 线程锁with self.lock保证并发场景下随机状态不被破坏;
  5. 性能优化:当itemsize == sizeof(np.npy_intp)(最常见情况)时,借助 gcc 生成专用实现,可获得约 33% 的性能提升。

PR #18328 修复的正是这些 C 层分布函数(含_shuffle_raw底层实现)在 C++ 编译环境下因缺少extern "C"链接约定而可能出现的链接失败或符号错乱问题。该问题在混合编译(C/C++ 混编)的构建链中尤为隐蔽。

相关测试:对象数组不被打散、只读保护

同样在 numpy/random/tests/test_random.py 中,还有两类相关测试:

  • test_shuffle_no_object_unpacking:对dtype=object数组 shuffle 后,数组中元素的id()必须保持不变(即不误拆解对象、不误改引用计数)。测试还覆盖了ndarray子类场景:传统 API(RandomState)对一维数组子类调用 shuffle 时会发出UserWarning(提示"Shuffling a one dimensional array..."),而新 API(Generator)则给出正确结果;
  • test_shuffle_not_writeable:对只读数组 shuffle 必须抛出ValueError,匹配信息为'read-only'

这些测试共同锁定了 1.20.1 中shuffle的行为契约,防止后续版本再次引入同类回归。

其余 13 个修复:从核心函数到 f2py、类型注解与 CI

核心函数:isclose/allclose与 timedelta64 的兼容

PR #18337("BUG: Allow unmodified use of isclose, allclose, etc. with timedelta")允许iscloseallclose等函数在不修改参数的情况下直接用于timedelta64类型。

在 numpy/_core/numeric.py 与 numpy/_core/numeric.py 中可以看到这两个函数的定义:

def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): """ Returns True if two arrays are element-wise equal within a tolerance. The tolerance values are positive, typically very small numbers. The relative difference (`rtol` * abs(`b`)) and the absolute difference ... """ def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): """ Returns a boolean array where two arrays are element-wise equal within a tolerance. ... """

修复前,对timedelta64数组直接调用isclose/allclose会因内部类型处理不当而失败或需用户手工改 dtype;修复后用户可以直接使用默认参数比较时间间隔数组,无需任何预处理。这对涉及时间序列、采样间隔比较的科学计算场景是实质性的可用性改进。

DType 序列化:允许 pickle 所有相关 DType 类型

PR #18345("BUG: Allow pickling all relevant DType types/classes")修复了部分 DType 类型/类无法被 pickle 的问题。在 1.20 系列中,DType 系统正经历向新 DType 协议(相关设计参见 doc/neps/nep-0041-improved-dtype-support.rst)过渡,本次修复保证了所有相关 DType 类型与类均可被正常序列化,避免使用multiprocessingpickle传递 dtype 时出现TypeError

f2py:修复包装 F90 子例程的缺陷

PR #18354("BUG: Fix f2py bugs when wrapping F90 subroutines")与 PR #18356("MAINT: crackfortran regex simplify")共同处理了 f2py 相关问题:

  • #18354 修复了将 Fortran 90 子例程包装为 Python 可调用模块时的多个 bug;
  • #18356 简化了crackfortran中的正则表达式逻辑(crackfortran是 f2py 用于解析 Fortran 源码的核心模块,实现位于 numpy/f2py/crackfortran.py)。

对 Fortran 用户而言,升级 1.20.1 后可更可靠地包装 F90 代码。

类型注解与__init__修复

  • PR #18306"MAINT: Add missing placeholder annotations"):补充了缺失的占位类型注解(placeholder annotations),完善numpy__init__.pyi/_core相关类型桩文件(如 numpy/_core/init.pyi),对 mypy/pyright 用户更友好;
  • PR #18310"BUG: Fix typo innumpy.__init__.py"):修复了 numpy/init.py 中的一个拼写错误,该错误在特定导入路径下可能引发问题。

CI 与文档维护

  • PR #18336"CI: fix when GitHub Actions builds trigger, and allow ci skips"):修正 GitHub Actions 构建的触发时机,并允许通过提交信息跳过 CI;
  • PR #18353"CI: CircleCI seems to occasionally time out, increase the limit"):CircleCI 偶发超时,提高超时上限;
  • PR #18352"DOC: Change license date 2020 -> 2021"):将许可证版权年份由 2020 更新为 2021。

发布收尾

PR #18359("REL: Prepare for the NumPy 1.20.1 release")是标准的发布准备提交,负责更新版本号与发布元数据,标志着 1.20.1 分支冻结并进入发布流程。

升级建议与验证方式

对于使用 NumPy 1.20.0 的用户,尤其是以下三类场景,建议尽快升级到 1.20.1:

  1. 下游 C/C++/Fortran 扩展构建者:命中 distutils fake libraries 遍历缺陷、signed_char依赖缺失或threads.h误判的平台,升级后可恢复稳定构建;
  2. 随机数重度用户:对 memoryview 或dtype=object数组调用shuffle、或在混编环境下使用RandomState/Generator的程序,升级后行为恢复正确;
  3. 时间序列分析者:需要对timedelta64数组直接使用isclose/allclose的代码。

升级后可通过仓库自带测试集验证核心行为(需要已构建的开发环境):

python -m pytest numpy/random/tests/test_random.py -k "shuffle" python -m pytest numpy/random/tests/test_random.py -k "memoryview or not_writeable"

第一条命令覆盖shuffle的回归测试(含 memoryview、对象数组、只读保护),第二条进一步聚焦 memoryview 与只读数组场景;isclose/allclose对 timedelta 的验证可参考 numpy/_core/numeric.py 的文档示例运行。

小结

NumPy 1.20.1 虽然规模不大(15 个 PR、8 位贡献者),但精准地解决了 1.20.0 带来的两个高影响回归——distutils 构建缺陷与random.shuffle回归——并顺带修复了 f2py、threads.h探测、DType pickling、timedelta 比较等一系列问题。从本仓库源码与测试可以看到,每一个 BUG 修复都伴随对应的回归测试(如 numpy/random/tests/test_random.py 中的test_shuffle_memoryview),这正是 NumPy 版本发布流程中"快速修复 + 回归防护"机制的典型体现。对于仍停留在 1.20.0 或更早版本的用户,本版本是低风险、高收益的升级目标。

【免费下载链接】numpyThe fundamental package for scientific computing with Python.项目地址: https://gitcode.com/gh_mirrors/nu/numpy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询