【问题标题】:str.__getslice__ is not working as expected with negative stopstr.__getslice__ 没有按预期工作,负停止
【发布时间】:2017-10-15 08:53:04
【问题描述】:

我试图在 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) 返回了一个空字符串。为什么会这样?

【问题讨论】:

  • 这不是特定于字符串的;你会得到与foo = list(foo) 相同的行为。此外,对于strlisthelp(*.__getslice__) 明确提到不支持负索引。 foo[slice(10, -1)] 工作正常。

标签: python string python-2.7 indexing python-internals


【解决方案1】:

字符串是在 中实现的,所以如果您对 不太了解,答案就不是那么直截了当,但无论如何我都会尽力而为:

如果您直接调用__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 &lt; 0) j = 0;),然后因为它小于 start,它将被设置为 start (if (j &lt; 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)

假设指定长度的序列,调整开始/结束切片索引。超出范围的索引以与正常切片处理一致的方式进行裁剪。

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


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

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

【讨论】:

  • 这是一个很好的答案,尽管语言名称令人不安的标签链接:)
猜你喜欢
  • 2021-10-08
  • 1970-01-01
  • 2011-05-18
  • 1970-01-01
  • 2018-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-19
相关资源
最近更新 更多