已经在SO中搜索了相同的内容,也看到了CPython的github存储库;但无济于事。似乎任何控制流构造的源代码实现都是不可见的,但不清楚为什么?
特别需要获取CPython中“用于-控制流构造”的源代码。
在不知情的情况下,我所能做的就是在小代码上使用dis模块的dis(),导致产生FOR_ITER操作码,这对我来说是无法理解的。
该操作码也没有使我理解嵌套的for循环构造的工作原理,这也是我想研究其在源代码中的实现的原因。
>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
答案 0 :(得分:1)
考虑当前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
循环遍历的对象iterable
传递给for
。
代码:
iterable
指向stack of python objects的顶部; iter
指向由PyObject_GetIter调用返回的迭代器; 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
操作码使迭代器再次运行,这次在下一个元素上运行。您可以在原始清单中看到,每个JUMP_ABSOLUTE
都用相应的FOR_ITER
操作码的行号调用,从而使迭代成为可能。
This answer也是该主题的很好参考。
答案 1 :(得分:0)
该实现已添加到this commit中;这是关于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