据我了解,Python 中的 for x in a_generator: foo(x)
循环大致等同于:
try:
while True:
foo(next(a_generator))
except StopIteration:
pass
这表明是这样的:
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)
会做两件事:
- 不引发任何异常、段错误或类似问题
- 遍历
y
直到到达某个x
,其中should_inner_loop(x)
返回 truthy,然后在内部中循环它for
直到stop_inner_loop(thing)
返回 true。然后,外层循环从内层循环停止的地方继续。
从我承认不是很好的测试来看,它似乎像上面那样执行。但是,我在规范中找不到任何保证这种行为在解释器中保持不变的内容。有没有什么地方说或暗示我可以确定它会一直这样?它会导致错误或以其他方式执行吗? (即做一些不同于上面描述的事情
注意上面的等效代码取 self 自己的经验;我不知道它是否真的准确。这就是我问的原因。
最佳答案
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
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
正如您在 line 617 中看到的那样, 实现__iter__()
是PyObject_SelfIter
,您可以找到其实现 here . PyObject_SelfIter
简单地返回对象(即生成器)本身。
因此,当您嵌套两个循环时,它们都在同一个迭代器上进行迭代。
而且,正如你所说,他们只是在打电话 next()
在上面,所以它是安全的。
但要小心:内循环会消耗外循环不会消耗的项目。 即使那是你想要做的,它也可能不是很可读。
如果这不是您想要的,请考虑 itertools.tee()
,它缓冲迭代器的输出,允许你迭代它的输出两次(或更多)。只有当 tee 迭代器在输出流中彼此靠近时,这才有效率;如果一个 tee 迭代器在使用另一个之前将完全耗尽,最好只调用 list
在迭代器上从中具体化一个列表。
关于python - 在 Python 中安全地循环遍历同一生成器中的生成器吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37284048/