2009-07-16 20 views
1

最近,我尝试从正在运行的生成器之外设置局部变量。生成器代码也应该访问这些变量。将本地变量添加到正在运行的生成器

一个问题是,当访问这些变量时,它解释了该解释器认为它必须是一个全局变量,因为该变量未在本地范围内设置。但我不想改变全局变量,也不想复制整个全局范围,以便将变量虚拟化为局部变量。

另一个麻烦是,它接近当地人(和全局?)的字典从外部访问时接受只读的字典。

是否有任何法律(或至少部分合法的方式)将当地人引入正在运行的生成器实例?

编辑澄清:

我不是说“发送”功能。这当然是一个简洁的函数,但是因为我想用不同的名称设置多个变量,所以对于我的目的来说这并不是很方便。

回答

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 

更新:好了,现在你已经更新了你的问题,让我有这个问题的另一个刺。也许这就是你的意思?

>>> 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 2009-07-17 08:41:54

+0

好的,我现在增加了`locals()`的另一个版本。也许这适合你的需求? – Stephan202 2009-07-17 14:26:23

1

如果你想有一个协同程序或一个发电机,也可以作为一个接收器,你应该使用send方法,如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函数转换为适当的装饰器。

相关问题