最近,我尝试从正在运行的生成器外部设置局部变量。生成器代码也应该访问这些变量。
一个问题是,当访问变量时,它会解释为解释器认为它必须是全局的,因为变量未在本地范围中设置。但我不想改变全局变量,也不想复制整个全局范围,以使变量人为地局部化。
另一个麻烦是,当从外部访问时,它本地(以及全局变量?)的字典被接合为只读。
是否有任何合法(或至少部分合法的方式)将本地人引入正在运行的生成器实例?
编辑以澄清:
我不是指“发送”功能。这当然是一个简洁的功能,但由于我想设置多个具有不同名称的变量,因此不符合我的目的。
答案 0 :(得分: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
更新:好的,既然你已经更新了你的问题,那么让我再试一次这个问题吧。也许这就是你的意思?
>>> 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 :(得分:1)
locals()始终返回只读字典。您可以创建自己的“本地人”字典:
def gen_func():
lcls = {}
for i in range(5):
yield (i, lcls)
print lcls
for (val, lcls) in gen_func():
lcls[val] = val
任何其他可变结构也将起作用。
答案 2 :(得分:1)
如果你想让coroutine或者一个生成器也充当接收器,你应该使用send方法,如Stephan202's answers中所示。如果你想通过设置生成器中的各种属性来改变运行时行为,那么Raymond Hettinger就会有一个旧的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
函数转换为适当的装饰器应该不会太困难。