由于range对象会按需生成值,这是否意味着在对范围进行索引时,都会调用迭代协议直到该索引?
我的意思是什么时候:
>>> R = range(1,11)
>>> print(R[5])
6
由于R[5]
未存储在内存中,是否每次都会通过创建新的迭代器来计算?如果没有,如何索引范围对象?
答案 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
当您编写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
检查它所传递的参数(R
和5
),以确定是否请求单个索引或切片。整数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 )