已经搜索了相同的搜索,并看到了cpython的github存储库;但无济于事。似乎不可见任何控制流构建体的源代码实现,但尚不清楚为什么?
尤其需要获取cpython中的" for -control Flow构造"的源代码。
面对一无所知,我所能做的就是在小型代码上使用dis模块的dis(),导致for_iter opcode,这是我无法理解的。
此操作码也不会使我理解嵌套的循环结构的工作,这是我想在源代码中研究相同的原因。
>import dis
def foo():
for i in range(3):
for j in range(2):
print(i,j)
dis.dis(foo)
3 0 SETUP_LOOP 44 (to 46)
2 LOAD_GLOBAL 0 (range)
4 LOAD_CONST 1 (3)
6 CALL_FUNCTION 1
8 GET_ITER
>> 10 FOR_ITER 32 (to 44)
12 STORE_FAST 0 (i)
4 14 SETUP_LOOP 26 (to 42)
16 LOAD_GLOBAL 0 (range)
18 LOAD_CONST 2 (2)
20 CALL_FUNCTION 1
22 GET_ITER
>> 24 FOR_ITER 14 (to 40)
26 STORE_FAST 1 (j)
5 28 LOAD_GLOBAL 1 (print)
30 LOAD_FAST 0 (i)
32 LOAD_FAST 1 (j)
34 CALL_FUNCTION 2
36 POP_TOP
38 JUMP_ABSOLUTE 24
>> 40 POP_BLOCK
>> 42 JUMP_ABSOLUTE 10
>> 44 POP_BLOCK
>> 46 LOAD_CONST 0 (None)
48 RETURN_VALUE
在此提交中添加了实现;这是有关FOR_ITER
的部分:
case FOR_ITER:
/* before: [iter]; after: [iter, iter()] *or* [] */
v = TOP();
x = PyObject_CallObject(v, NULL);
if (x == NULL) {
if (PyErr_ExceptionMatches(
PyExc_StopIteration))
{
PyErr_Clear();
x = v = POP();
Py_DECREF(v);
JUMPBY(oparg);
continue;
}
break;
}
PUSH(x);
continue;
忽略倒数,for x in y:
循环等同于以下Python代码:
# GET_ITER
y_iter = iter(y)
# FOR_ITER
while True:
try:
x = next(y_iter)
except StopIteration:
break
# body of for loop
pass
考虑当前CPYTHON代码基础上的主题(3.8.5):
您可以在拆卸中看到每个FOR_ITER
都有GET_ITER
。
get_iter源代码(检查编号注释):
case TARGET(GET_ITER): {
/* before: [obj]; after [getiter(obj)] */
PyObject *iterable = TOP(); // 1.
PyObject *iter = PyObject_GetIter(iterable); // 2.
Py_DECREF(iterable); // 3.
SET_TOP(iter); // 4.
if (iter == NULL)
goto error;
PREDICT(FOR_ITER);
PREDICT(CALL_FUNCTION);
DISPATCH();
}
GET_ITER
实际上传递到PyObject_GetIter
由for
循环穿越的对象iterable
。
代码:
- 将
iterable
点到python对象的堆栈顶部; - 使
iter
点点到由PyObject_getiter Call返回的迭代器; - 将参考计数减少到
iterable
; - 迭代器
iter
现在位于堆栈顶部。
PyObject_GetIter
检查峰值是否是迭代器(即消耗迭代的东西),如果是的,则返回它。如果不是,请检查是否是序列。如果是序列,则将其转换为迭代器。迭代器是返回的值。
for_iter代码:
case TARGET(FOR_ITER): {
PREDICTED(FOR_ITER);
/* before: [iter]; after: [iter, iter()] *or* [] */
PyObject *iter = TOP(); // 1.
PyObject *next = (*iter->ob_type->tp_iternext)(iter); // 2.
if (next != NULL) {
PUSH(next); // 3.
PREDICT(STORE_FAST);
PREDICT(UNPACK_SEQUENCE);
DISPATCH();
}
if (_PyErr_Occurred(tstate)) {
if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
goto error;
}
else if (tstate->c_tracefunc != NULL) {
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
}
_PyErr_Clear(tstate);
}
/* iterator ended normally */
STACK_SHRINK(1);
Py_DECREF(iter);
JUMPBY(oparg);
PREDICT(POP_BLOCK);
DISPATCH();
}
感兴趣的部分:
- 从堆栈顶部获取迭代器;
- 获得
next (i.e. tp_iternext)
方法调用的结果; - 如果不是
NULL
,则将结果推入堆栈。
您应该问的一件事:这仅涵盖循环的单个迭代。使迭代器穿越所有项目的代码在哪里?
是JUMP_ABSOLUTE
OpCode,使迭代器再次运行,这次是在下一个元素上。您可以在原始列表中看到每个JUMP_ABSOLUTE
都与相应的FOR_ITER
OPCODE的行号调用,从而使迭代成为可能。
这个答案也是关于此主题的很好的参考。