# Range 的實現原理
老規矩,先看看 range 的 docstring
range(stop) -> range object  | |
range(start, stop[, step]) -> range object  | |
Return an object that produces a sequence of integers from start (inclusive)  | |
to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.  | |
start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.  | |
These are exactly the valid indices for a list of 4 elements.  | |
When step is given, it specifies the increment (or decrement).  | 
首先,調用 range () 時,它會返回一個 range object,那在 CPython 裏,對應的就是 rangeobject 結構體,但最終返回的還是 PyObject *,range 對應 CPython 中 PyRange_Type,位於 Objects/rangeobject.c
PyTypeObject PyRange_Type = {  | |
PyVarObject_HEAD_INIT(&PyType_Type, 0)  | |
"range", /* Name of this type */  | |
sizeof(rangeobject), /* Basic object size */  | |
0, /* Item size for varobject */  | |
(destructor)range_dealloc, /* tp_dealloc */  | |
0, /* tp_vectorcall_offset */  | |
0, /* tp_getattr */  | |
0, /* tp_setattr */  | |
0, /* tp_as_async */  | |
(reprfunc)range_repr, /* tp_repr */  | |
&range_as_number, /* tp_as_number */  | |
&range_as_sequence, /* tp_as_sequence */  | |
&range_as_mapping, /* tp_as_mapping */  | |
(hashfunc)range_hash, /* tp_hash */  | |
0, /* tp_call */  | |
0, /* tp_str */  | |
PyObject_GenericGetAttr, /* tp_getattro */  | |
0, /* tp_setattro */  | |
0, /* tp_as_buffer */  | |
Py_TPFLAGS_DEFAULT, /* tp_flags */  | |
range_doc, /* tp_doc */  | |
0, /* tp_traverse */  | |
0, /* tp_clear */  | |
range_richcompare, /* tp_richcompare */  | |
0, /* tp_weaklistoffset */  | |
range_iter, /* tp_iter */  | |
0, /* tp_iternext */  | |
range_methods, /* tp_methods */  | |
range_members, /* tp_members */  | |
0, /* tp_getset */  | |
0, /* tp_base */  | |
0, /* tp_dict */  | |
0, /* tp_descr_get */  | |
0, /* tp_descr_set */  | |
0, /* tp_dictoffset */  | |
0, /* tp_init */  | |
0, /* tp_alloc */  | |
range_new, /* tp_new */  | |
.tp_vectorcall = (vectorcallfunc)range_vectorcall  | |
};  | 
在調用 range (10)、range (0, 10)、range (0, 10, 2) 的時候,首先會執行 range_new 方法
static PyObject *  | |
range_new(PyTypeObject *type, PyObject *args, PyObject *kw)  | |
{ | |
if (!_PyArg_NoKeywords("range", kw))  | |
	// 校驗是否沒有傳遞關鍵字,如果沒有傳遞,表示真,傳遞了,為假 | |
return NULL;  | |
    /* | |
創建 rangeobject 並返回,_PyTuple_ITEMS 將 args 轉爲 PyTupleObject, 並取出 ob_item  | |
PyTyple_GET_SIZE 獲取參數的數量  | |
*/  | |
return range_from_array(type, _PyTuple_ITEMS(args), PyTuple_GET_SIZE(args));  | |
} | 
那在創建 range 對象時,主要是執行了 range_from_array 方法,一切事情都發生在裏面
static PyObject *  | |
range_from_array(PyTypeObject *type, PyObject *const *args, Py_ssize_t num_args)  | |
{ | |
rangeobject *obj; // 首先聲明一個 rangeobject 對象,如果創建成功就會返回此對象  | |
PyObject *start = NULL, *stop = NULL, *step = NULL; // 定義 range 的起始、結尾、間隔  | |
    // 參數個數 | |
switch (num_args) {  | |
case 3:  | |
	    // 傳遞了 3 個參數: start、stop、step | |
step = args[2]; // 取出第 3 个参数:间隔  | |
            /* fallthrough */ | |
case 2:  | |
	    // 傳遞了 2 個參數: start、stop | |
            /* Convert borrowed refs to owned refs */ | |
start = PyNumber_Index(args[0]); // 校验类型并返回索引,方法实现在下方  | |
if (!start) {  | |
		// 为空,不是有效的 int | |
return NULL;  | |
            } | |
stop = PyNumber_Index(args[1]); // 校验类型并返回索引  | |
if (!stop) {  | |
		// 为空,不是有效的 int | |
Py_DECREF(start); // 引用计数 - 1  | |
return NULL;  | |
            } | |
	    // 验证间隔,实现方法在下面 | |
step = validate_step(step); /* Caution, this can clear exceptions */  | |
if (!step) {  | |
		// 如果为空 | |
Py_DECREF(start); //start 引用计数 - 1  | |
Py_DECREF(stop); //stop 引用计数 - 1  | |
return NULL; // 结束  | |
            } | |
break;  | |
case 1:  | |
	    // 傳遞了 1 個參數: stop | |
stop = PyNumber_Index(args[0]); // 校验类型並返回索引  | |
if (!stop) {  | |
return NULL;  | |
            } | |
Py_INCREF(_PyLong_Zero);  | |
start = _PyLong_Zero;  | |
Py_INCREF(_PyLong_One);  | |
step = _PyLong_One;  | |
break;  | |
case 0:  | |
	    // 儅 range 傳遞參數為 0 個時,抛異常 | |
PyErr_SetString(PyExc_TypeError,  | |
"range expected at least 1 argument, got 0");  | |
return NULL;  | |
default:  | |
	    // 儅 range 傳遞參數超過 3 個時,抛異常 | |
PyErr_Format(PyExc_TypeError,  | |
"range expected at most 3 arguments, got %zd",  | |
num_args);  | |
return NULL;  | |
    } | |
obj = make_range_object(type, start, stop, step); // 真正開始創建 rangeobject, 裏面做了計算 range 的範圍長度  | |
if (obj != NULL) {  | |
	// 創建 rangeobject 成功,返回 PyObject * | |
return (PyObject *) obj;  | |
    } | |
    /* Failed to create object, release attributes */ | |
    // 創建失敗,釋放屬性 | |
Py_DECREF(start);  | |
Py_DECREF(stop);  | |
Py_DECREF(step);  | |
return NULL;  | |
} | |
static PyObject *  | |
validate_step(PyObject *step)  | |
{ | |
    // 校验间隔 | |
    /* No step specified, use a step of 1. */ | |
if (!step)  | |
	// 如果没有指定间隔,就默认使用 1 | |
return PyLong_FromLong(1); // 从 C long int 中创建一个 Python int 对象  | |
step = PyNumber_Index(step); // 校验类型并返回索引  | |
if (step && _PyLong_Sign(step) == 0) {  | |
	// 如果间隔给了 0, 抛异常 | |
PyErr_SetString(PyExc_ValueError,  | |
"range() arg 3 must not be zero");  | |
Py_CLEAR(step);  | |
    } | |
return step; // 返回步长  | |
} | 

PyNumber_Index 方法的定义,返回 Python int 对象,如果结果不是 int,就会引发 TypeError,或者对象不能作为索引
PyObject * | |
PyNumber_Index(PyObject *item)  | |
{ | |
PyObject *result = NULL;  | |
if (item == NULL) {  | |
return null_error();  | |
    } | |
if (PyLong_Check(item)) {  | |
	// 如果 item 是一個 PyLongObject 或者子類,則返回 True | |
Py_INCREF(item); // 引用計數 + 1  | |
return item;  | |
    } | |
if (!_PyIndex_Check(item)) {  | |
	// 如果對象不是一個 PyLongObject,檢查對象是否定義了__index__方法 | |
PyErr_Format(PyExc_TypeError,  | |
                     "'%.200s' object cannot be interpreted " | |
"as an integer", Py_TYPE(item)->tp_name);  | |
return NULL;  | |
    } | |
    // 在 item 的父類中,獲取 tp_as_number (實現了數字協議對象相關的結構), 並取出 np_index 調用,對應 Python 對象的__index__方法, | |
result = Py_TYPE(item)->tp_as_number->nb_index(item);  | |
if (!result || PyLong_CheckExact(result))  | |
	// 如果返回非 0 或者為 PyLongObject | |
return result; // 成功返回  | |
if (!PyLong_Check(result)) {  | |
	// 如果返回的不是一個 PyLongObject, 抛異常,比如在 Python 中返回 None 或者非 int 對象 | |
PyErr_Format(PyExc_TypeError,  | |
"__index__ returned non-int (type %.200s)",  | |
Py_TYPE(result)->tp_name);  | |
Py_DECREF(result); // 引用計數 - 1  | |
return NULL;  | |
    } | |
    /* Issue #17576: warn if 'result' not of exact type int. */ | |
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,  | |
            "__index__ returned non-int (type %.200s).  " | |
            "The ability to return an instance of a strict subclass of int " | |
"is deprecated, and may be removed in a future version of Python.",  | |
Py_TYPE(result)->tp_name)) {  | |
Py_DECREF(result);  | |
return NULL;  | |
    } | |
return result;  | |
} | 
在 PyNumber_Index 中,如果對象不是 PyLongObject,還會檢查對象是否定義了__index__,在沒有重載的情況下作爲參數傳遞給 range

如果重載了__index__方法的情況下,range 成功創建了 rangeobject

如果重載了__index__方法,但是返回的不是 PyLongObject,就會抛異常: “index returned non-int (type %.200s)”

# for … in range 時,發生了什麽?
在 Python 中,對一個對象進行循環迭代時,首先會調用對象的__iter__方法,獲取 iterator,再對 iterator 一直調用__next__方法,獲取 iterable,直到遇到 StopIteration,就會停止,那麽在對 rangeobject 進行迭代時,就是執行了 rangeobject 定義好的__iter__方法,對應 range_iter 方法
static PyObject *  | |
range_iter(PyObject *seq)  | |
{ | |
rangeobject *r = (rangeobject *)seq;  | |
longrangeiterobject *it; // 在調用__iter__時,如果大小範圍超出,返回的是 longrangeiterobject, 儅調用__next__時,實際就是調用它的__next__方法,而不是 rangeobject  | |
long lstart, lstop, lstep; // 起始、結束、間隔  | |
unsigned long ulen;  | |
assert(PyRange_Check(seq));  | |
    // If all three fields and the length convert to long, use the int | |
......  | |
    /* | |
get_len_of_range 主要計算 range 範圍 / 長度  | |
如果 step > 0 并且 start >= stop,或者 step < 0 并且 start <= stop, 那麽返回的範圍為空 -> []  | |
對於 step > 0, 如果 n 個值在範圍内,則最後一個值為 start + (n - 1) * step && <= stop - 1  | |
1. 儅 step (間隔) > 0 && start (起始) < stop (結束) 時,1 + (stop - 1 - start) /step, 如 1 + (10 - 1 - 0) / 1 = 10  | |
2. 儅 step < 0 && start > stop 時,1 + (start - 1 - stop) / (0 - (-step)), 如 1 + (0 - 1 - 10) / (0 - (-1)) = -10  | |
3. 否則返回 0, 也就是空範圍  | |
*/  | |
ulen = get_len_of_range(lstart, lstop, lstep);  | |
if (ulen > (unsigned long)LONG_MAX) {  | |
	// 如果計算的範圍長度大於最大範圍, | |
goto long_range;  | |
    } | |
    /* check for potential overflow of lstart + ulen * lstep */ | |
if (ulen) {  | |
if (lstep > 0) {  | |
if (lstop > LONG_MAX - (lstep - 1))  | |
goto long_range;  | |
        } | |
else {  | |
if (lstop < LONG_MIN + (-1 - lstep))  | |
goto long_range;  | |
        } | |
    } | |
    // 範圍一般都不會太大,一般都會進入該方法,這裏返回的是 rangeiterobject, 而不是 longrangeiterobject | |
return fast_range_iter(lstart, lstop, lstep, (long)ulen);  | |
  long_range: | |
    // 超出最大值,返回 longrangeiterobject | |
it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type);  | |
if (it == NULL)  | |
return NULL;  | |
it->start = r->start; // 起始  | |
it->step = r->step; // 結尾  | |
it->len = r->length; // 長度 / 範圍  | |
it->index = _PyLong_Zero; // 索引  | |
Py_INCREF(it->start); // 引用計數 + 1  | |
Py_INCREF(it->step);  | |
Py_INCREF(it->len);  | |
Py_INCREF(it->index);  | |
return (PyObject *)it;  | |
} | |
static PyObject *  | |
fast_range_iter(long start, long stop, long step, long len)  | |
{ | |
rangeiterobject *it = PyObject_New(rangeiterobject, &PyRangeIter_Type);  | |
if (it == NULL)  | |
return NULL;  | |
it->start = start;  | |
it->step = step;  | |
it->len = len;  | |
it->index = 0; // 索引默認為 0  | |
return (PyObject *)it;  | |
} | 
關於 get_len_of_range 的注釋説明,是我找女同事翻譯的,也不一定都對,但是特別感謝她😜
如果 step > 0 且 lo >= hi,或 step <0 且 lo <= hi,则范围为空。否则对于 step> 0,如果 n 个值在范围内,则最后一个是
lo + (n-1) step,它必须 <= hi-1。 重新排列,n <= (hi - lo - 1)/step + 1,所以拿 RHS 的底层给出适当的值。 因为 lo < hi 在这种情况下, hi-lo-1 >= 0,所以 RHS 是非负数,因此截断与底层一样。 令 M 为最大的正长整型,RHS 分子最坏的情况是 hi=M, lo=-M-1, 然后 hi-lo-1 = M-(-M-1)-1 = 2M。 因此 无符号长整型 就足够精确计算 RHS 的精准度。 step < 0 的分析很相似。
儅執行 for … in range (10) 時,它才開始計算範圍,如果範圍沒有超出最大值,那麽返回的對象為 rangeiterobject,那麽此時,在調用__next__時,就是調用 rangeiterobject 的__next__方法,rangeiterobject 對應 PyRangeIter_Type, __next__會調用 rangeiter_next
static PyObject *  | |
rangeiter_next(rangeiterobject *r)  | |
{ | |
    // 每調用一次__next__, 就會執行這裏 | |
if (r->index < r->len)  | |
	// 如果 index 小於計算的範圍,index 默認為 0 | |
        /* cast to unsigned to avoid possible signed overflow | |
in intermediate calculations. */  | |
	// 返回一個 PyLongObject,每次計算為 | |
	// 假設 range (10), 那麽第 1 次,start 為 0, index 為 0, step 默認為 1 | |
	// 第 1 次: 0 + 0 * 1 = 0 | |
	// 第 2 次: 0 + 1 * 1 = 1 | |
	// 第 10 次: 0 + 9 * 1 = 9 | |
	// 結果為: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 | |
return PyLong_FromLong((long)(r->start +  | |
(unsigned long)(r->index++) * r->step));  | |
return NULL;  | |
} | 
range 的基本流程基本就這些了,還有一些 count、index 方法,我基本沒用過,有興趣可以自己去探討一下