Accessing Local Variables Inside a Python Generator
How would you access a local variable defined inside a Python generator from outside the generator?
I have a case where my generator manipulates a local state, and for unittests I want to inspect this state to ensure it contains the correct values.
I can't store the state to an instance variable (e.g. self.state = blah), because I might be creating multiple generators from the same class instance, meaning the generators might overwrite each other's state. I also can't return the state in the yield expression, because the state name may change or vary because individual generator instances.
e.g. I want to do something like this (albeit this code doesn't work)
from random import random
class MyIter(object):
def __iter__(self):
context = {}
for i in xrange(10):
context[random()] = random()
yield i
obj = MyIter()
i1 = iter(obj)
i2 = iter(obj)
while 1:
try:
i1.next()
i2.next()
print i1.context
print i2.context
except StopIteration:
开发者_如何学JAVAbreak
Is there anyway to access local variables by inspecting Python's execution stack?
Sorry to answer my own question, but after digging into the generator interface, I found the exact path I need to access the generator's local variables:
from random import random
class MyIter(object):
def __iter__(self):
context = {}
for i in xrange(10):
context[random()] = random()
yield i
obj = MyIter()
i1 = iter(obj)
i2 = iter(obj)
while 1:
try:
i1.next()
i2.next()
print i1.gi_frame.f_locals['context']
print i2.gi_frame.f_locals['context']
except StopIteration:
break
You should be treating the generator as a black box. Unit tests shouldn't care about its internal state, because that's just an implementation detail; they should only care about the specified behavior.
If you really want to do this, separate the iterator class from the container class:
from random import random
class MyContainer(object):
def __iter__(self):
return MyIter(self)
class MyIter(object):
def __init__(self, container):
self.container = container
self.context = {}
self.it = iter(xrange(10))
def next(self):
self.context[random()] = random()
return next(self.it)
def __iter__(self):
return self
obj = MyContainer()
# ...
I don't consider this very useful though...
精彩评论