为啥在 python 切片中允许非整数内置类型?

Posted

技术标签:

【中文标题】为啥在 python 切片中允许非整数内置类型?【英文标题】:Why are non integral builtin types allowed in python slices?为什么在 python 切片中允许非整数内置类型? 【发布时间】:2017-01-18 20:53:20 【问题描述】:

我刚刚改进了必须支持切片的 library 的测试覆盖率,我注意到切片可以包含非整数类型:

>>> slice(1, "2", 3.0)
slice(1, '2', 3.0)
>>> sl = slice(1, "2", 3.0)
>>> [1,2,3][sl]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: slice indices must be integers or None or have an __index__ method

这可能只是我的静态类型背景,但我觉得很奇怪没有__index__ 的内置类型可以在没有TypeError 的情况下传入这里。为什么会这样?假设允许任意类型以支持实现__index__ 的类型的鸭子类型,我是否正确?对于最常用的情况,是否由于性能原因而缺少类型检查?

在PEP 357 之前,示例中的切片是否无效?

【问题讨论】:

带浮点数的切片语法可以有效地描述 n 维空间中的边界框,这在某些情况下会很方便。自定义类型可以接受这样的切片。 这是一个非常有趣的浮动用例,我没有想到。为什么允许使用字符串? 你也可以在切片中使用Ellipsis。 @LaurentLAPORTE:你可以使用省略号;不过,我从未见过它完成。在 NumPy 中,a[3, ..., 5] 之类的东西,... 不是切片的一部分。 @Mark Dickinson:我不记得我在哪里看到的,但省略号仅用于 Numpy ;-) 【参考方案1】:

第三方库可能希望为他们自己的对象实现切片,核心语言没有理由将这些第三方库限制为仅使用整数或类似整数的对象(即,其类型提供 @ 987654321@ 方法)在他们的切片中。以下是在切片中使用非整数的包的两个值得注意的示例:在 NumPy 中,一些对象接受复杂的步骤,例如:

>>> import numpy
>>> numpy.mgrid[0:2:5j]
array([ 0. ,  0.5,  1. ,  1.5,  2. ])

在 Pandas 中,您可以按标签对 SeriesDataframe 对象进行切片。该标签可以是字符串,也可以是 datetime 对象(例如)。

>>> import pandas
>>> s = pandas.Series(range(4), index=['a', 'b', 'c', 'd'])
>>> s['b':'d']
b    1
c    2
d    3
dtype: int64

因此,核心语言在构造包含非整数的切片时引发异常是没有意义的;这会破坏上述库。相反,如果切片组件(开始、停止、步进)的类型不合适,则实际的切片操作应该引发异常。

【讨论】:

【参考方案2】:

假设允许任意类型以支持实现 __index__ 的类型的鸭子类型,我是否正确?

在初始化slice 对象时为什么要限制传递的类型并没有真正的理由。正如PEP 357numpy 的理性所述,它使用的数字类型不能从对象继承,因此对传递的类型进行严格的issubclass 检查将使它们无法用作索引值。所以使用了鸭子类型,如果它定义了适当的方法(__index__),它是可用的。

还请注意,这(__index__ 的存在)仅在应用切片时强制执行(如您所见,TypeError__getitem__ 期间引发,即list_subscript)操作时PySlice_GetIndicesEx被调用以尝试获取您传递的值。

slice objects initializer 不区分它接受什么类型,所有PyObject 都可以应用,从它的签名可以看出:

PyObject *
PySlice_New(PyObject *start, PyObject *stop, PyObject *step)

   /* rest omitted */

PEP 357 之前示例中的切片是否无效?

我刚刚构建了一个2.4 版本的 Python 并对其进行了测试(如果我没记错的话,PEP 357 出现在 2.5 中),再次检查参数是否为数字不是在初始化期间而是在 @987654341 @ 被调用;唯一不同的是异常消息没有对__index__ dunder(显然不存在)发出任何通知:

Python 2.4 (#1, Sep 11 2016, 18:13:11) 
[GCC 5.4.0 20160609] on linux4
Type "help", "copyright", "credits" or "license" for more information.
>>> s = slice(0, Ellipsis)
>>> [1, 2, 3][s]
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: slice indices must be integers

【讨论】:

以上是关于为啥在 python 切片中允许非整数内置类型?的主要内容,如果未能解决你的问题,请参考以下文章

Python零散知识点

为啥列表中允许使用尾随逗号?

为啥通过切片分配到列表末尾之后不会引发 IndexError? [复制]

为啥python的列表切片不会产生索引越界错误? [复制]

Python---数据类型---列表

如何在 Python 中排除特定类型的输入变量?