[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

Reply via email to