Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 357 – 允许使用任何对象进行切片

作者:
Travis Oliphant <oliphant at ee.byu.edu>
状态:
最终版
类型:
标准轨迹
创建:
2006年2月9日
Python 版本:
2.5
历史记录:


目录

摘要

本 PEP 提出在PyNumberMethods中添加一个nb_index槽位,以及一个__index__特殊方法,以便在 Python 中需要整数的地方(例如切片语法,槽位名称由此而来)使用任意对象。

基本原理

目前,整数和长整数在切片中扮演着特殊的角色,因为它们是切片语法中唯一允许的对象。换句话说,如果 X 是一个实现了序列协议的对象,那么X[obj1:obj2]仅在obj1obj2都是整数或长整数时才有效。没有办法让obj1obj2告诉 Python 它们可以合理地用作序列的索引。这是一个不必要的限制。

例如,在 NumPy 中,有 8 种不同的整数标量,分别对应于 8 位、16 位、32 位和 64 位的无符号和有符号整数。这些类型对象可以在许多 Python 期望真正整数但由于内存布局不兼容而无法从 Python 整数类型继承的地方合理地用作整数。应该有一种方法能够告诉 Python 一个对象可以像整数一样工作。

无法将nb_int(以及__int__特殊方法)用于此目的,因为该方法用于将对象强制转换为整数。允许每个可以强制转换为整数的对象在 Python 期望真正整数的任何地方都用作整数是不合适的。例如,如果使用__int__在切片中将对象转换为整数,则浮点数对象将被允许在切片中使用,并且x[3.2:5.8]不会像预期的那样引发错误。

提案

PyNumberMethods中添加一个nb_index槽位,以及一个相应的__index__特殊方法。对象可以定义一个函数放在nb_index槽位中,该函数返回一个 Python 整数(int 或 long)。然后,每当 Python 需要(例如在PySequence_GetSlicePySequence_SetSlicePySequence_DelSlice中)一个Py_ssize_t值时,可以将此整数适当地转换为Py_ssize_t值。

规范

  1. nb_index槽位将具有以下签名
    PyObject *index_func (PyObject *self)
    

    返回的对象必须是 Python IntType或 Python LongType。如果发生错误,则应返回 NULL,并设置相应的错误。

  2. __index__特殊方法将具有以下签名
    def __index__(self):
        return obj
    

    其中 obj 必须是 int 或 long。

  3. 将添加 3 个新的抽象 C-API 函数
    1. 第一个检查对象是否支持索引槽位,以及它是否已填充。
      int PyIndex_Check(obj)
      

      如果对象定义了nb_index槽位,则将返回 true。

    2. 第二个是围绕nb_index调用的简单包装器,如果调用不可用或它没有返回 int 或 long,则会引发PyExc_TypeError。因为PyIndex_Check是在PyNumber_Index调用内部执行的,所以您可以直接调用它并管理任何错误,而不是首先检查兼容性。
      PyObject *PyNumber_Index (PyObject *obj)
      
    3. 第三个调用有助于处理实际需要从对象获取Py_ssize_t值以用于索引或其他用途的常见情况。
      Py_ssize_t PyNumber_AsSsize_t(PyObject *obj, PyObject *exc)
      

      如果可用,该函数将调用 obj 的nb_index槽位,然后将返回的 Python 整数转换为Py_ssize_t值。如果一切顺利,则返回该值。第二个参数允许控制如果从nb_index返回的整数无法放入Py_ssize_t值中会发生什么。

      如果 exc 为 NULL,则返回值将被裁剪到PY_SSIZE_T_MAXPY_SSIZE_T_MIN,具体取决于 obj 的nb_index槽位返回正整数还是负整数。如果 exc 不为 NULL,则它是将被设置为替换PyExc_OverflowError的错误对象,当 Python 整数或长整数转换为Py_ssize_t时,会引发该错误。

  4. 将添加一个新的operator.index(obj)函数,该函数将调用等效于obj.__index__()的内容,如果 obj 没有实现特殊方法,则会引发错误。

实施计划

  1. object.h中添加nb_index槽位,并修改typeobject.c以创建__index__方法
  2. ceval.c中的ISINT宏更改为ISINDEX,并修改它以适应定义了索引槽位的对象。
  3. 更改_PyEval_SliceIndex函数以适应定义了索引槽位的对象。
  4. 更改所有内置对象(例如列表),这些对象使用as_mapping槽位进行下标访问,并使用对整数的特殊检查来检查槽位。
  5. nb_index槽位添加到整数和长整数(它们只返回自身)
  6. 添加PyNumber_Index C-API 以从具有nb_index槽位的任何 Python 对象返回一个整数。
  7. 添加operator.index(x)函数。
  8. 更改arrayobject.cmmapmodule.c以使用新的 C-API 进行其下标访问和其他需求。
  9. 添加单元测试

讨论问题

速度

实现不应减慢 Python 的速度,因为用作索引的整数和长整数将在相同数量的指令中完成。唯一变化的是,过去会生成错误的内容现在将被接受。

为什么不使用nb_int,它已经存在了?

nb_int方法用于强制转换,因此其含义与这里请求的内容从根本上不同。本 PEP 提出了一种方法,用于已经可以被认为是整数的对象在 Python 需要整数时向其传达该信息。使用nb_int将是一件坏事的最重要例子是浮点数对象已经定义了nb_int方法,但浮点数对象不应用作序列中的索引。

为什么名称为__index__

有人提出了关于__index__名称的问题,因为槽位还有其他可能的解释。例如,该槽位可以在 Python 内部需要整数的任何时候使用(例如在"mystring" * 3中)。Guido 建议使用此名称,因为切片语法是拥有此类槽位的最主要原因,最终没有出现更好的名称。有关建议名称(例如“__discrete__”和“__ordinal__”)的示例,请参阅讨论线程[1]

为什么从nb_index返回PyObject *

最初,Py_ssize_t被选为nb_index槽位的返回类型。但是,这导致无法跟踪和区分溢出和下溢错误,而无需使用丑陋且脆弱的技巧。由于nb_index槽位至少以 3 种不同的方式用于 Python 核心(获取整数、获取切片终点和获取序列索引),因此需要相当大的灵活性来处理所有这些情况。能够灵活处理所有用例非常重要。例如,最初返回nb_indexPy_ssize_t的实现导致发现,在具有 >=2GB RAM 的 32 位机器上,s = 'x' * (2**100)有效,但len(s)被裁剪为 2147483647。提出了几种修复方法,但最终决定nb_index需要返回一个类似于nb_intnb_long槽位的 Python 对象,以便正确处理溢出。

为什么__index__不能返回任何具有nb_index方法的对象?

这将以许多不同的方式允许无限递归,这些方式不容易检查。此限制类似于__nonzero__返回 int 或 bool 的要求。

参考实现

作为补丁 1436368 提交到 SourceForge。

参考文献


来源:https://github.com/python/peps/blob/main/peps/pep-0357.rst

上次修改:2023-09-09 17:39:29 GMT