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 отслеживает, где в исполнении генератора он находится,Я полагаю, что он использует f_lasti (== "последняя инструкция") атрибута gi_frame генератора (генераторы, в отличие от обычных функций, несут с собой кадр выполнения).Вот небольшой инструментарий, показывающий, как меняются значения:
>>> 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 соответствует пронумерованной строке в разобранном коде, на котором был последний результат: он перезапускается с этой точкигенератор снова включен.