切片端点不可见地截断


>>> 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__ 通常对 long 不安全?

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

编辑:CPython 中截断切片的代码在哪里? 这是python(语言(规范的一部分,还是只是cpython(实现(的"功能"?

处理

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

对于双元素切片,Python 2 使用SLICE+*操作码之一;然后由 apply_slice() 函数处理。这使用 _PyEval_SliceIndex 函数将 Python 索引对象(intlong 或任何实现 __index__ 方法的东西(转换为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.
*/

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

使用三值形式(item[start:stop:stride](的切片使用BUILD_SLICE操作码代替(后跟BINARY_SUBSCR(,这反而创建了一个slice()对象而不限制sys.maxsize

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

至于这是语言的实现细节或一部分:Ssliceings表达式文档区分simple_slicingextended_slicing;前者只允许short_slice形式。对于简单切片,索引必须是纯整数

下限和上限表达式(如果存在(的计算结果必须为纯整数;默认值分别为零和sys.maxint

这表明 Python 2 语言将索引限制为sys.maxint值,不允许长整数。在Python 3中,简单切片已经从语言中完全删除。

如果您的代码必须支持使用超出sys.maxsize的值进行切片,并且您必须从实现__getslice__的类型继承,则您的选项是:

  • 使用三值语法,None表示步幅:

    Potato()[123:x:None]
    
  • 显式创建slice()对象:

    Potato()[slice(123, x)]
    

slice()对象可以很好地处理long整数;但是slice.indices()方法仍然无法处理超过sys.maxsize长度:

>>> import sys
>>> s = slice(0, sys.maxsize + 1)
>>> s
slice(0, 9223372036854775808L, None)
>>> s.stop
9223372036854775808L
>>> s.indices(sys.maxsize + 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: cannot fit 'long' into an index-sized integer

最新更新