# 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 方法,我基本沒用過,有興趣可以自己去探討一下