Извините за задержку.
Shed не использовал никогда, но “правдоподобно показать что происходит внутри”, имхо, он не сумеет.
Давайте просто из самого питона посмотрим для начала
Примитивный генератор:
>>> def f():
... for i in range(10):
... yield i
Запустим его и посмотрим, что вернет:
>>> i = f()
>>> dir(i)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', __repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']
>>> i.gi_code
<code object f at 0x22fce40, file "<input>", line 2>
>>> i.gi_frame
<frame object at 0x2422c30>
>>> i.next()
0
>>> i.next()
1
>>> i.gi_frame
<frame object at 0x2422c30>
фрейм - тот же самый объект.
Давайте глянем, что у него внутри:
>>> dir(i.gi_frame)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'f_back', 'f_builtins', 'f_code', 'f_exc_traceback', 'f_exc_type', 'f_exc_value', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_restricted', 'f_trace']
Самое интересное на данный момент - локальные переменные:
>>> i.gi_frame.f_locals
{'i': 1}
>>> i.next()
2
>>> i.gi_frame.f_locals
{'i': 2}
>>>
>>> i.gi_frame.f_lasti
22
Видно, как они меняются.
i.gi_frame.f_lasti - адрес последней выполненной инструкции. В этом примере она, понятное дело, всегда будет 22 - yield же один единственный и генератор всегда останавливается на одном и том же месте (только до первого i.next() там лежит -1 - генератор не начинает работу автоматически после создания, его нужно дернуть один раз).
Чуть более сложный пример:
>>> def g():
... for i in range(10):
... if i % 2:
... yield i
... else:
... yield i
...
>>> j = g()
>>> j.gi_frame.f_lasti
-1
>>> j.next()
0
>>> j.gi_frame.f_lasti
42
>>> j.next()
1
>>> j.gi_frame.f_lasti
33
>>> j.next()
2
>>> j.gi_frame.f_lasti
42
>>> j.next()
3
>>> j.gi_frame.f_lasti
33
>>>
Теперь, если требуется, можно посмотреть в исходники питона и таки разобраться, как генератор работает с фреймом.
P.S. xrange - это не генератор, а такой себе класс. Поддерживающий протокол итератора и контейнера, и считающийся лениво
>>> z = xrange(10)
>>> z
xrange(10)
>>> type(z)
<type 'xrange'>
>>> dir(z)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__iter__', '__len__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__reversed__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> z[5]
5
>>> z[9]
9
>>> z[3]
3
>>>