索引范围对象时是否使用迭代协议?

时间:2018-11-21 22:45:05

标签: python python-3.x indexing python-internals

由于range对象会按需生成值,这是否意味着在对范围进行索引时,都会调用迭代协议直到该索引?

我的意思是什么时候:

>>> R = range(1,11)
>>> print(R[5])
6

由于R[5]未存储在内存中,是否每次都会通过创建新的迭代器来计算?如果没有,如何索引范围对象?

2 个答案:

答案 0 :(得分:5)

这里没有创建迭代器,也没有迭代发生。实现range对象是为了使Python能够按需按需计算恒定时间R[5]的值。 1

如果索引i不为负,则计算可归结为:

i * step + start

因此,对于您的代码R[5],这将是5*1 + 1,即6。

如果索引i为负,则将R的长度首先添加到i中,然后像以前一样计算值:

(i + len(R)) * step + start

Python内部的

当您编写R[5]时,此Python语法最终会转换为对PyObject_GetItem的调用,该调用将检查对象R以查看应如何继续查找索引5的项目

PyObject_GetItem首先检查range类型的tp_as_mapping slot。这不为空;它包含对名为range_as_mapping的结构的引用。 PyObject_GetItem然后检查该结构的mp_subscript field中的内容:

static PyMappingMethods range_as_mapping = {
        (lenfunc)range_length,       /* mp_length */
        (binaryfunc)range_subscript, /* mp_subscript */
        (objobjargproc)0,            /* mp_ass_subscript */
};

如您在上面的代码段中所见,它发现range_subscript函数占据了mp_subscript字段。 2

现在range_subscript检查它所传递的参数(R5),以确定是否请求单个索引或切片。整数5意味着只需要一个索引,因此该函数将值的计算委托给compute_range_item。该函数执行计算以返回该答案第一部分中概述的整数6。


1 我假设您正在使用CPython:其他Python实现可能会以不同的方式实现range对象。

2 如果要调用len(R),则可以在mp_length中看到内部函数,该内部函数被调用来计算R的长度(参见Why is "1000000000000000 in range(1000000000000001)" so fast in Python 3?

答案 1 :(得分:-1)

不。不是。

但是range同时支持迭代协议和索引(通过 getitem