据我所知,for x in a_generator: foo(x)Python中的循环大致相当于:
try:
while True:
foo(next(a_generator))
except StopIteration:
pass
Run Code Online (Sandbox Code Playgroud)
这表明这样的事情:
for outer_item in a_generator:
if should_inner_loop(outer_item):
for inner_item in a_generator:
foo(inner_item)
if stop_inner_loop(inner_item): break
else:
bar(outer_item)
Run Code Online (Sandbox Code Playgroud)
会做两件事:
y,直到它到达一些x地方should_inner_loop(x)返回truthy,然后在它循环在内部for,直到stop_inner_loop(thing)返回true.然后,外环恢复内部停止的位置.从我公认的不太好的测试来看,它似乎如上所述.但是,我在规范中找不到任何保证此行为在解释器中保持不变的内容.有没有说过或暗示我可以确定它总是这样的?它会导致错误,还是以其他方式执行?(即做一些不同于上述内容的事情
NB以上代码取自我自己的经验; 我不知道它是否真的准确.这就是我要问的原因.
TL; DR:CPython是安全的(但是我找不到任何相关规范),尽管它可能不会做你想做的事情.
首先,让我们谈谈你的第一个假设,即等价.
for循环实际上首先调用iter()对象,然后运行next()其结果,直到它得到一个StopIteration.
这是相关的字节码(Python的低级形式,由解释器本身使用):
>>> import dis
>>> def f():
... for x in y:
... print(x)
...
>>> dis.dis(f)
2 0 SETUP_LOOP 24 (to 27)
3 LOAD_GLOBAL 0 (y)
6 GET_ITER
>> 7 FOR_ITER 16 (to 26)
10 STORE_FAST 0 (x)
3 13 LOAD_GLOBAL 1 (print)
16 LOAD_FAST 0 (x)
19 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
22 POP_TOP
23 JUMP_ABSOLUTE 7
>> 26 POP_BLOCK
>> 27 LOAD_CONST 0 (None)
30 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
GET_ITER调用iter(y)(它本身调用y.__iter__())并将其结果推送到堆栈上(将其视为一堆本地未命名变量),然后进入循环FOR_ITER,调用next(<iterator>)(它本身调用<iterator>.__next__()),然后执行循环内的代码,然后执行JUMP_ABSOLUTE让执行回来FOR_ITER.
现在,为安全起见:
以下是生成器的方法:https://hg.python.org/cpython/file/101404/Objects/genobject.c#l589
正如您在第617行所见,__iter__()is PyObject_SelfIter的实现,您可以在此处找到它的实现.PyObject_SelfIter简单地返回对象(即生成器)本身.
因此,当您嵌套两个循环时,两者都迭代在同一个迭代器上.而且,正如你所说,他们只是在呼唤next()它,所以它是安全的.
但要小心:内部循环将消耗外部循环不会消耗的项目.即使这是你想要做的,它可能不是很可读.
如果这不是您想要做的,请考虑itertools.tee()缓冲迭代器的输出,允许您迭代其输出两次(或更多).只有当tee迭代器在输出流中保持彼此靠近时,这才有效.如果一个tee迭代器在使用另一个之前将完全耗尽,最好只需调用list迭代器来实现一个列表.