str.__getslice__ 负止损未按预期工作

str.__getslice__ is not working as expected with negative stop

我正在尝试 运行 遵循 python 2.7 中的代码:

foo = "python is awesome"
print foo[10:16]
print foo.__getslice__(10,16)
print foo[10:-1]
print foo.__getslice__(10,-1)

除最后一个打印的 "awsome" 外全部。但是 foo.__getslice__(10,-1) 返回了一个空字符串。为什么会这样?

字符串是在 so the answer isn't that straight-forward to understand if you don't know much about and the 中实现的,但我还是尽力了:

如果您直接调用 __getslice__,您将使用 string_slice:

static PyObject *
string_slice(PyStringObject *a, Py_ssize_t i, Py_ssize_t j)
     /* j -- may be negative! */
{
    if (i < 0)
        i = 0;
    if (j < 0)
        j = 0; /* Avoid signed/unsigned bug in next line */
    if (j > Py_SIZE(a))
        j = Py_SIZE(a);
    if (i == 0 && j == Py_SIZE(a) && PyString_CheckExact(a)) {
        /* It's the same as a */
        Py_INCREF(a);
        return (PyObject *)a;
    }
    if (j < i)
        j = i;
    return PyString_FromStringAndSize(a->ob_sval + i, j-i);
}

这里i是开始索引,j是停止索引。如果 stop 小于零,它将被设置为 0 (if (j < 0) j = 0;),然后因为它小于 start 它将被设置为 start (if (j < i) j = i;)。所以你最终得到 start=10 和 stop=10,这只是一个空字符串。

但是,如果您使用 [],您将调用 string_subscript(我将只包含该方法的相关部分):

static PyObject*
string_subscript(PyStringObject* self, PyObject* item)
{
    /* ... */
    if (PySlice_Check(item)) {
        Py_ssize_t start, stop, step, slicelength, cur, i;
        /* ... */

        if (_PySlice_Unpack(item, &start, &stop, &step) < 0) {
            return NULL;
        }
        slicelength = _PySlice_AdjustIndices(PyString_GET_SIZE(self), &start,
                                            &stop, step);

        /* ... */
        if (step == 1) {
            return PyString_FromStringAndSize(
                PyString_AS_STRING(self) + start,
                slicelength);
        }
        /* ... */
    }
    /* ... */
}

正确地 使用 _PySlice_AdjustIndices 调整索引(就像 PySlice_AdjustIndices)。该函数会将 -1 的停止转换为 len(string) - 1:

的停止

Py_ssize_t PySlice_AdjustIndices(Py_ssize_t length, Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t step)

Adjust start/end slice indices assuming a sequence of the specified length. Out of bounds indices are clipped in a manner consistent with the handling of normal slices.

实际调用的函数可能与那个函数不同。但我相信文档适用于两者。


但您通常不应直接调用 __*__ 方法。所以我不知道这是 Python 中的错误还是预期用途(据我所知,它可能是针对某些类型切片的优化函数)。

然而 __getslice__ 很久以前就被弃用了 - 最好完全远离它。