[Neal D. Becker]
> ...
> Only one problem. Is there any way to access the state of a
> generator externally? In other words, the generator saves all it's
> local variables. Can an unrelated object then query the values of
> those variables? (In this case, to get at intermediate results)
It's the same as asking whether you can peek at the internal state of
any function -- "a generator" in CPython is really just a Python
function stack frame, essentially the same as a non-generator's stack
frame. The primary difference is that when a function returns, its
frame is decref'ed and typically gets garbage-collected then; when a
generator yields, its frame isn't decref'ed, and the
generator-iterator holds on to the frame for resumption. In this
sense, Python's generators are quite literally "resumable functions".
Python-the-language doesn't define any way to peek inside one function
from another. CPython-the-implementation can be exploited, as a
generator-iterator in CPython has a gi_frame attribute referencing the
frame. You can pick that apart in Python like any other CPython stack
frame. For example, here's a toy program:
"""
def fib(a, b):
i = 0
yield a
i = 1
yield b
while True:
started_loop = True
i, a, b = i+1, b, a+b
yield b
if b > 12:
break
f = fib(0, 1)
for val in f:
print val, f.gi_frame.f_locals
"""
and here's its output:
0 {'i': 0, 'a': 0, 'b': 1}
1 {'i': 1, 'a': 0, 'b': 1}
1 {'i': 2, 'a': 1, 'b': 1, 'started_loop': True}
2 {'i': 3, 'a': 1, 'b': 2, 'started_loop': True}
3 {'i': 4, 'a': 2, 'b': 3, 'started_loop': True}
5 {'i': 5, 'a': 3, 'b': 5, 'started_loop': True}
8 {'i': 6, 'a': 5, 'b': 8, 'started_loop': True}
13 {'i': 7, 'a': 8, 'b': 13, 'started_loop': True}
Note some subtleties: despite possible appearance, it's *not* the
case that the local vrbl 'started_loop' doesn't exist before it's
assigned to. Local variables are wholly determined at compile time,
and all exist as soon as a function is entered. Locals aren't
normally represented internally in a dict, either. That you see a
dict at all here, and that it suppresses entries for unbound locals,
is all the result of fancy code executed as a side effect of
referencing the "f_locals" attribute of a frame (f_locals is akin to a
Python property with a "getter" function).
Note too that, as when getting a dict via the locals() builtin inside
a function, mutating the dict has no defined effect (it may or may not
have any visible effect, depending on accidents that aren't, and never
will be, documented or guaranteed).
--
http://mail.python.org/mailman/listinfo/python-list