>>> 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(实现)的“特性”?
答案 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索引对象(int
,long
或实现__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