NEP 34 — 禁止从序列推断 dtype=object#

作者:

Matti Picus

状态:

最终

类型:

标准跟踪

创建日期:

2019-10-10

决议:

https://mail.python.org/pipermail/numpy-discussion/2019-October/080200.html

摘要#

当用户使用序列的序列创建数组时,有时会错误地匹配嵌套序列的长度,这通常被称为“不规则数组”(ragged arrays)。在此,我们将其称为不规则嵌套序列。目前,通过不带 dtype 关键字参数的 np.array([<ragged_nested_sequence>]) 创建此类数组,会默认生成一个 object-dtype 数组。本提案旨在将此行为更改为引发 ValueError 异常。

动机与范围#

用户通过 np.array 创建 numpy.ndarray 时,如果指定列表的列表(lists-of-lists),可能会错误地传入长度不同的列表。目前,我们接受这种输入并自动创建一个 dtype=object 的数组。这可能令人困惑,因为这很少是用户所期望的结果。将自动 dtype 检测更改为对于不规则嵌套序列(定义为递归的序列的序列,其中同一层的所有序列长度不都相同)不再返回 object,将迫使那些确实希望创建 object 数组的用户明确指定。请注意,liststuplesnd.ndarrays 都是序列[0]。例如,参见 问题 5303

用法与影响#

此更改后,使用不规则嵌套序列创建数组时必须明确定义 dtype

>>> np.array([[1, 2], [1]])
ValueError: cannot guess the desired dtype from the input
>>> np.array([[1, 2], [1]], dtype=object)
# succeeds, with no change from current behaviour

此弃用将影响任何内部调用 np.asarray 的函数。例如,assert_equal 系列函数会调用 np.asarray,因此用户将不得不更改类似以下的代码:

np.assert_equal(a, [[1, 2], 3])

np.assert_equal(a, np.array([[1, 2], 3], dtype=object))

详细描述#

要明确设置对象数组的形状(因为有时很难确定所需的形状),可以使用:

>>> arr = np.empty(correct_shape, dtype=object)
>>> arr[...] = values

我们还将拒绝非序列和序列的混合序列,例如以下所有情况都将被拒绝:

>>> arr = np.array([np.arange(10), [10]])
>>> arr = np.array([[range(3), range(3), range(3)], [range(3), 0, 0]])

实现#

要更改的代码位于 PyArray_GetArrayParamsFromObject 内部以及内部的 discover_dimensions 函数中。PR 14794 中的首次实现导致了许多下游库的故障,并在 1.18 版本发布之前被回滚。随后,下游库修复了它们使用不规则数组的地方。重新实现的工作成为了 PR 15119,该 PR 已合并到 1.19 版本中。

向后兼容性#

任何依赖于从不规则嵌套序列创建对象数组的用户都需要修改其代码。将有一个弃用期,在此期间当前行为将发出 DeprecationWarning 警告。

替代方案#

  • 我们可以维持现状。

  • 也有建议在数组创建中添加一个 kwarg depth,或者添加另一个数组创建 API 函数 ragged_array_object。目的是消除从 array([[1, 2], [1]], dtype=object) 创建对象数组时的歧义:返回的数组应该是 (1,) 还是 (2,) 的形状?本 NEP 不处理此问题,仅弃用在不规则嵌套序列中使用不带 dtype=objectarray。不规则嵌套序列的用户将来可能面临另一个弃用周期。理由是:我们预计很少有用户打算那样使用不规则数组,这从未被视为 NumPy 数组的一个用例。用户可能更适合使用另一个库或者直接使用列表的列表。

  • 也有人建议弃用所有自动创建 object-dtype 数组的行为,这将要求对诸如 np.array([Decimal(10), Decimal(10)]) 这样的情况显式添加 dtype=object。这也不属于当前 NEP 的范围。理由是:评估这一更大变化的影响更困难,我们不确定这可能会影响多少用户。

讨论#

问题 5303 的评论表明,早在 2014 年这就被认为是意外行为。此后几年中也提出了改变的建议,但均未实现。在 PR 14794 中的 WIP(进行中)实现似乎指明了这种方法的可行性。

参考文献和脚注#