Добавить локальную переменную в работающий генератор - PullRequest
1 голос
/ 17 июля 2009

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

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

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

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

Редактировать для уточнения:

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

Ответы [ 3 ]

5 голосов
/ 17 июля 2009

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

>>> 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

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

>>> 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() генератору.Я не вижу возможности обойти это ...

1 голос
/ 17 июля 2009

Если вы хотите иметь сопрограмму или генератор, который также действует как приемник, вы должны использовать метод отправки, как в Ответы Stephan202 .Если вы хотите изменить поведение во время выполнения, установив различные атрибуты в генераторе, есть старый рецепт Рэймонда Хеттингера:

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 в правильный декоратор.

1 голос
/ 17 июля 2009

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

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


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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...