当使用索引时,范围是否必须计算所有先前的值?

5
在Python 3中,range支持索引,但我想知道它是如何工作的。
例如:range(100000000000000000000000000)[-1] 我基本了解range函数实际上返回一个占用有限内存的range对象。这是否意味着要获取最后一个值,必须计算出所有先前的值?

3
那并不是切片符号。切片使用冒号':'。那只是一种特殊的索引/下标形式。 - Shashank
哦,你说得对!这是一个愚蠢的错误。 - PaulC
2个回答

8
不需要获取先前的值即可获取最后一个值。它由compute_item函数计算(该函数由compute_range_item调用 <- range_item ...)。 < p >来自Python 3.3源代码(Objects/rangeobjects.c)

static PyObject *
compute_item(rangeobject *r, PyObject *i)
{
    PyObject *incr, *result;
    /* PyLong equivalent to:
     *    return r->start + (i * r->step)
     */
    incr = PyNumber_Multiply(i, r->step);
    if (!incr)
        return NULL;
    result = PyNumber_Add(r->start, incr);
    Py_DECREF(incr);
    return result;
}

5
为什么timeit.timeit('range(9999999999999)[-1]')的执行时间比timeit.timeit('range(9999999999999)[1]')要长近30%? - aIKid
1
@aIKid,负索引应该转换为正索引。我没有对其进行分析(现在无法进行)。这可能是一个原因。[请参见源代码中的此行](http://hg.python.org/cpython/file/d8e352e2f110/Objects/rangeobject.c#l260)。 - falsetru
2
@aIKid,还有另一个可能的原因。 range(9999999999999)[-1] 返回9999999999998,而range(9999999999999)[1] 返回11占用28字节(在CPython中进行了内部化),9999999999998占用32字节。(CPython 3.3.2 64位)。多次分配/释放对象可能需要一些时间。 - falsetru

2

可能不是:

import timeit
print(timeit.timeit('range(10)[-1]'))
>>> 0.5438045680057257

print(timeit.timeit('range(100000000000000000000000000)[-1]'))
>>> 0.7502937959798146

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接