2009-07-16 50 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函數轉換爲適當的裝飾器。

相關問題