static stmt_ty
ast_for_for_stmt(struct compiling *c, const node *n)
{
asdl_seq *_target, *seq = NULL, *suite_seq;
expr_ty expression;
expr_ty target, first;
/* ... */
node_target = CHILD(n, 1);
_target = ast_for_exprlist(c, node_target, Store);
if (!_target)
return NULL;
/* Check the # of children rather than the length of _target, since
for x, in ... has 1 element in _target, but still requires a Tuple. */
first = (expr_ty)asdl_seq_GET(_target, 0);
if (NCH(node_target) == 1)
target = first;
else
target = Tuple(_target, Store, first->lineno, first->col_offset, c->c_arena);
/* ... */
return For(target, expression, suite_seq, seq, LINENO(n), n->n_col_offset,
c->c_arena);
}
在调用函数ast_for_exprlist时创建了Store上下文,该函数为索引变量创建了一个结点(注意,for循环的索引变量还可能是一序列变量的元组,而不仅仅是一个变量)。
在介绍为什么for循环变量和循环中的其他变量一视同仁的过程中,这个函数是最后总要的一部分。在AST中进行标记之后,在符号表和虚拟机中用于处理循环变量的代码与处理其他变量的代码是相同的。
结束语
本文讨论了Python中可能被认为是“疑难杂症”的某些特定行为。我希望这篇文章确实解释了Python的变量和作用域的代码执行行为,说明了为什么这些行为是有用的而且永远不太可能改变,以及Python编译器的内部如何使其正常工作。感谢您的阅读!
[1] 在这里,我很想开个Microsoft Visual C ++ 6的玩笑,但事实让人有些不安,因为在2015年这个博客的大部分读者不会懂这个笑话(这反映了我的年龄,而不是我的读者的能力)。
[2] 你可能会说,在执行到break之前时,dowithstuff(i)可以进入if中。但是,这并不总是很方便。此外,根据Guido的解释,这里对我们关注的问题做了一个很好的分离——循环被用于并只用于搜索。在搜索结束后,循环中的变量会发生什么已经不是循环关注的事情。我觉得这是非常好的一点。
[3]: 通常我的文章中的代码是基于Python 3。具体而言,我期待Python库中将要完成的下一个版本(3.5)的default分支。但是对于这个特定的主题,在3.x系列中的任何版本的源代码都应该是可以工作的。
[4] 函数分解中另一件很明显的事是,如果循环不执行,为什么i仍然是不可见的,GET_ITER和FOR_ITER这对字节码将我们的循环当做一个迭代器,然后调用其__next__方法。如果这个调用最后以抛出StopIteration异常结束,虚拟机捕捉到这个异常然后结束循环。只有实际值被返回,虚拟机才会继续对i执行STORE_FAST,因此让这个值存在,让后续代码可以引用。
[5] 这是一个奇怪的设计,我怀疑这个设计的实质是为了使用相对干净的递归访问AST中的代码,如符号表代码和CFG生成器。
本文原创发布php中文网,转载请注明出处,感谢您的尊重!