2011-01-19 2 views
5

Как получить доступ к локальной переменной, определенной внутри генератора 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 
+2

Ой! Не делайте этого, это деталь реализации CPython. Он может меняться с более новыми версиями и быть различным для разных реализаций Python (например, Jython или IronPython). И это не очень читаемо ... –

+0

Я вряд ли когда-либо использую Jython или IronPython, и у обоих из них есть много несовместимостей со стандартными функциями CPython, но они хорошо взяты. – Cerin

+0

+1 Хотя я никогда не буду полагаться на какой-либо код, который должен получить доступ к внутреннему состоянию генератора во время нормальной работы, * очень полезно иметь возможность получить эти данные во время отладки, учитывая, что было несколько раз, когда Питон оставил меня в недоумении: «Что ты даже думал?» (Кроме того, я бы никогда не почувствовал себя плохо в ответе на свой вопрос, если только ответ был неправильным.;)) – Augusta

0

Вы должны рассматривать генератор как черный ящик. Модульные тесты не должны заботиться о его внутреннем состоянии, потому что это всего лишь деталь реализации; они должны заботиться только о указанном поведении.

0

Если вы действительно хотите сделать это, разделите класс итератора из класса контейнера:

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() 
# ... 

Я не считаю, что это очень полезно, хотя ...

+0

Спасибо. Это очень чистый и OO подход, хотя он добавляет немного дополнительной сложности. – Cerin