切片端点不可见地被截断

时间:2014-09-01 01:26:17

标签: python python-2.7 cpython

>>> class Potato(object):
...    def __getslice__(self, start, stop):
...       print start, stop
...         
>>> sys.maxint
9223372036854775807
>>> x = sys.maxint + 69
>>> print x
9223372036854775876
>>> Potato()[123:x]
123 9223372036854775807

为什么对getslice的调用不尊重我发送的stop,而是默默地替换2 ^ 63 - 1?这是否意味着为您自己的语法实现__getslice__通常会对longs不安全?

无论如何,我可以用__getitem__做任何我需要的事情,我只是想知道为什么__getslice__显然已被打破。

编辑: CPython中截断切片的代码在哪里?这是python(语言)规范的一部分还是cpython(实现)的“特性”?

1 个答案:

答案 0 :(得分:7)

处理实现sq_slice插槽的对象的切片的Python C代码无法处理Py_ssize_t(== sys.maxsize上的任何整数。 sq_slice广告位是__getslice__特殊方法的等效C-API。

对于双元素切片,Python 2使用SLICE+* opcodes之一;然后由apply_slice() function处理。这使用_PyEval_SliceIndex function将Python索引对象(intlong或实现__index__ method的任何内容)转换为Py_ssize_t整数。该方法有以下注释:

/* Extract a slice index from a PyInt or PyLong or an object with the
   nb_index slot defined, and store in *pi.
   Silently reduce values larger than PY_SSIZE_T_MAX to PY_SSIZE_T_MAX,
   and silently boost values less than -PY_SSIZE_T_MAX-1 to -PY_SSIZE_T_MAX-1.
   Return 0 on error, 1 on success.
*/

这意味着使用2值语法在Python 2中进行的任何切片仅限于sys.maxsize范围内提供sq_slice插槽时的值。

使用三值表单(item[start:stop:stride])进行切片会改为使用BUILD_SLICE opcode(后跟BINARY_SUBSCR),而是创建slice() object而不限制为{{1} }}

如果对象未实现sys.maxsize个插槽(因此不存在sq_slice()),__getslice__函数也会回退到使用apply_slice()对象。

至于这是一个实现细节或语言的一部分:Slicings expression documentation区分slice()simple_slicing;前者仅允许extended_slicing形式。对于简单切片,索引必须是普通整数

  

下限和上限表达式(如果存在)必须求值为普通整数;默认值分别为零和short_slice

建议 Python 2 语言将索引限制为sys.maxint值,禁止长整数。在Python 3中,完全从语言中删除了简单的切片。

如果您的代码必须支持使用sys.maxint 之外的值进行切片,则必须从实现sys.maxsize的类型继承,那么您的选项是:

  • 使用三值语法,步长为__getslice__

    None
  • 明确创建Potato()[123:x:None] 个对象:

    slice()

Potato()[slice(123, x)] 个对象可以正常处理slice()个整数;但slice.indices() method无法处理超过long的长度:

sys.maxsize