Доступ к локальным переменным внутри генератора Python
Как бы вы получили доступ к локальной переменной, определенной внутри генератора Python извне генератора?
У меня есть случай, когда мой генератор манипулирует локальным состоянием, и для unittests я хочу проверить это состояние, чтобы убедиться, что оно содержит правильные значения.
Я не могу сохранить состояние в переменной экземпляра (например, self.state = blah), потому что я могу создавать несколько генераторов из одного экземпляра класса, то есть генераторы могут перезаписывать каждый состояние другого. Я также не могу вернуть состояние в выражении yield, потому что имя состояния может изменяться или изменяться из-за отдельных экземпляров генератора.
например, я хочу сделать что-то подобное (хотя этот код не работает)
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:
break
есть ли доступ к локальным переменным путем проверки стека выполнения Python?
3 ответов
извините за ответ на мой собственный вопрос, но после копания в интерфейсе генератора я нашел точный путь, который мне нужен для доступа к локальным переменным генератора:
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
вы должны рассматривать генератор как черный ящик. Модульные тесты не должны заботиться о своем внутреннем состоянии, потому что это просто деталь реализации; они должны заботиться только об определенном поведении.
Если вы действительно хотите это сделать, отделите класс итератора от класса контейнера:
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()
# ...
Я не считаю это очень полезным, хотя...