2009-07-16 3 views
1

В последнее время я попытался установить локальные переменные извне работающего генератора. Код генератора также должен иметь доступ к этим переменным.Добавить локальную переменную в работающий генератор

Одна из проблем заключалась в том, что при доступе к переменным он стыкуется, что интерпретатор думал, что он должен быть глобальным, поскольку переменная не была установлена ​​в локальной области. Но я не хочу изменять глобальные переменные и не хочу копировать всю глобальную область, чтобы сделать переменные искусственно локальными.

Другая проблема заключалась в том, что это означает, что словари для местных жителей (и глобалы?), Сглаженные, должны быть доступны только для чтения при доступе извне.

Существует ли какой-либо законный (или, по крайней мере, частично законный) способ введения местных жителей в экземпляр работающего генератора?

Edit для уточнения:

Я не имею в виду функцию "отправить". Это, конечно, аккуратная функция, но поскольку я хочу установить несколько переменных с разными именами, это не удобно для моих целей.

ответ

0

locals() всегда возвращает постоянный адрес для чтения. Вы можете создать свой собственный «местный» словарь:

def gen_func(): 
    lcls = {} 
    for i in range(5): 
     yield (i, lcls) 
     print lcls 


for (val, lcls) in gen_func(): 
    lcls[val] = val 

Любая другая изменяемая структура также будет работать.

5

Что вы можете ищите, это метод send, который позволяет использовать значение , отправленное в генератор. The reference дает пример:

>>> def echo(value=None): 
...  print "Execution starts when 'next()' is called for the first time." 
...  try: 
...   while True: 
...    try: 
...     value = (yield value) 
...    except Exception, e: 
...     value = e 
...  finally: 
...   print "Don't forget to clean up when 'close()' is called." 
... 
>>> generator = echo(1) 
>>> print generator.next() 
Execution starts when 'next()' is called for the first time. 
1 
>>> print generator.next() 
None 
>>> print generator.send(2) 
2 
>>> generator.throw(TypeError, "spam") 
TypeError('spam',) 
>>> generator.close() 
Don't forget to clean up when 'close()' is called. 

Позвольте мне привести пример моей. (Берегись Код выше Python 2.6, но ниже я буду писать Python 3; py3k ref!):

>>> def amplify(iter, amp=1): 
...  for i in iter: 
...   reply = (yield i * amp) 
...   amp = reply if reply != None else amp 
... 
>>> it = amplify(range(10)) 
>>> next(it) 
0 
>>> next(it) 
1 
>>> it.send(3) # 2 * 3 = 6 
6 
>>> it.send(8) # 3 * 8 = 24 
24 
>>> next(it) # 4 * 8 = 32 
32 

Конечно, если вы действительно хотите, вы также можете сделать это без send. Например. инкапсулируя генератора внутри класса (но это не так элегантно!):

>>> class MyIter: 
...  def __init__(self, iter, amp=1): 
...   self.iter = iter 
...   self.amp = amp 
...  def __iter__(self): 
...   for i in self.iter: 
...    yield i * self.amp 
...  def __call__(self): 
...   return iter(self) 
... 
>>> iterable = MyIter(range(10)) 
>>> iterator = iterable() 
>>> next(iterator) 
0 
>>> next(iterator) 
1 
>>> iterable.amp = 3 
>>> next(iterator) 
6 
>>> iterable.amp = 8 
>>> next(iterator) 
24 
>>> next(iterator) 
32 

Update: Хорошо, теперь, когда вы обновили свой вопрос, дайте мне еще один удар по этой проблеме. Возможно, это то, что вы имеете в виду?

>>> def amplify(iter, loc={}): 
...  for i in iter: 
...   yield i * loc.get('amp', 1) 
... 
>>> it = amplify(range(10), locals()) 
>>> next(it) 
0 
>>> next(it) 
1 
>>> amp = 3 
>>> next(it) 
6 
>>> amp = 8 
>>> next(it) 
24 
>>> next(it) 
32 

Обратите внимание, что locals() следует рассматривать как только для чтения и зависит сфера. Как вы можете видеть, вам нужно явно передать locals() генератору. Я не вижу этого ...

+0

Спасибо за очень длинные примеры. Это швы, я забыл добавить комментарий о «отправке» здесь в первом редактировании моего вопроса. – Juergen

+0

Хорошо, теперь я добавил еще одну версию с 'locals()'. Возможно, это соответствует вашим потребностям? – Stephan202

1

Если вы хотите иметь сопрограмму или генератор, который также действует как приемник, вы должны использовать метод отправки, как в Stephan202's answers.Если вы хотите изменить поведение во время выполнения с помощью настройки различных атрибутов в генераторе, есть старый recipe Раймонд Хеттингер:

def foo_iter(self): 
    self.v = "foo" 
    while True: 
     yield self.v 

enableAttributes(foo_iter) 
it = foo_iter() 
print it.next() 
it.v = "boo" 
print it.next() 

Это будет печатать:

foo 
boo 

Оно не должно быть слишком трудно преобразуйте функцию enableAttributes в соответствующий декоратор.

+0

Привет, Торстен, спасибо, это очень близко к тому, что я хотел сделать. Я не знал об этом рецепте, и мне кажется, что это очень интересно для меня (нужно осмотреть ближе в тихий час). Тем не менее было бы неплохо иметь что-то более удобное внутри генераторного кодирования ... – Juergen

Смежные вопросы