yield 返回传递给它的任何对象,即使该对象是序列或生成器或其他迭代器。因此:
>>> def g():
... yield [1,2,3]
... yield 1
... yield 2
... yield 3
...
>>> gen = g()
>>> gen.next()
[1, 2, 3]
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
3
>>> gen.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
__iter__ 在需要对对象内容的迭代器时在对象上调用(例如当它是for x in obj 构造的一部分时)。您可以使用yield 创建生成器(因为生成器是迭代器),但在本示例中您不需要这样做。以下内容也将起作用:
def __iter__(self):
return iter(self.__a)
如果你想使用yield,并且你想让Vect对象的迭代器在向量的内容中移动,你必须分别产生每个值:
def __iter__(self):
for i in self.__a:
yield i
yield 表示 __iter__ 将返回一个生成器,并且在生成器对象上调用 next() 将在它上次停止的点恢复函数,因为它会遍历 __a。
=======
针对有关 Python 如何跟踪生成器执行位置的附加问题,我相信它使用了生成器的 gi_frame 属性的 f_lasti(==“最后一条指令”)(生成器,与普通函数不同,随身携带一个执行框架)。这里有一些工具可以显示值如何变化:
>>> import dis
>>> def g():
... yield 1
... for i in range(10):
... yield i*2
...
>>> gen = g()
>>> dis.dis(gen.gi_code)
2 0 LOAD_CONST 1 (1)
3 YIELD_VALUE
4 POP_TOP
3 5 SETUP_LOOP 29 (to 37)
8 LOAD_GLOBAL 0 (range)
11 LOAD_CONST 2 (10)
14 CALL_FUNCTION 1
17 GET_ITER
>> 18 FOR_ITER 15 (to 36)
21 STORE_FAST 0 (i)
4 24 LOAD_FAST 0 (i)
27 LOAD_CONST 3 (2)
30 BINARY_MULTIPLY
31 YIELD_VALUE
32 POP_TOP
33 JUMP_ABSOLUTE 18
>> 36 POP_BLOCK
>> 37 LOAD_CONST 0 (None)
40 RETURN_VALUE
>>> gen.gi_frame.f_lasti ## -1 because we haven't started yet
-1
>>> gen.next()
1
>>> gen.gi_frame.f_lasti
3
>>> gen.gi_frame.f_locals
{}
>>> gen.next()
0
>>> gen.gi_frame.f_lasti , gen.gi_frame.f_locals
(31, {'i': 0})
>>> gen.next()
2
>>> gen.gi_frame.f_lasti , gen.gi_frame.f_locals
(31, {'i': 1})
>>>
请注意 f_lasti 值如何对应于反汇编代码中最后一次 yield 所在的编号行:它在重新进入生成器时从该点重新启动。