更新:我一直想要的是greenlets.
注意:随着人们的回答迫使我"提高赌注",这个问题发生了一些变异,因为我的琐碎例子有微不足道的简化; 根据亚历克斯的建议,我不会继续在这里改变它,而是当我把它更清楚时,我会把这个问题放在脑后.
Python生成器是一个美丽的东西,但我怎样才能轻松地将其分解为模块(结构化编程)?我实际上想要PEP 380,或至少在语法负担方面有所作为,但在现有的Python中(例如2.6)
作为一个(公认的愚蠢)例子,请采取以下措施:
def sillyGenerator():
for i in xrange(10):
yield i*i
for i in xrange(12):
yield i*i
for i in xrange(8):
yield i*i
Run Code Online (Sandbox Code Playgroud)
作为DRY的忠实信徒,我在这里发现了重复的模式并将其分解为一种方法:
def quadraticRange(n):
for i in xrange(n)
yield i*i
def sillyGenerator():
quadraticRange(10)
quadraticRange(12)
quadraticRange(8)
Run Code Online (Sandbox Code Playgroud)
......当然不起作用.父必须在循环中调用新函数,产生结果:
def sillyGenerator():
for i in quadraticRange(10):
yield i
for i in quadraticRange(12):
yield i
for i in quadraticRange(8):
yield i
Run Code Online (Sandbox Code Playgroud)
......比以前更长!
如果我想将一部分生成器推送到一个函数中,我总是需要这个相当冗长的两行包装器来调用它.如果我想支持send(),情况会变得更糟:
def sillyGeneratorRevisited():
g = subgenerator()
v = None
try:
while True:
v = yield g.send(v)
catch StopIteration:
pass
if v < 4:
# ...
else:
# ...
Run Code Online (Sandbox Code Playgroud)
而这并没有考虑到异常传递.每次都是相同的样板!然而,一个人不能应用DRY并将这个相同的代码考虑到函数中,因为......你需要样板来调用它!我想要的是:
def sillyGenerator():
yield from quadraticRange(10)
yield from quadraticRange(12)
yield from quadraticRange(8)
def sillyGeneratorRevisited():
v = yield from subgenerator()
if v < 4:
# ...
else:
# ...
Run Code Online (Sandbox Code Playgroud)
有没有人有这个问题的解决方案?我有第一次尝试,但我想知道其他人想出了什么.最终,任何解决方案都必须解决主生成器基于发送到生成器的数据结果执行复杂逻辑的示例,并且可能对子生成器进行大量调用:我的用例是用于实现的生成器长期运行的复杂状态机.
Ale*_*lli 11
但是,我想让我的可重用性标准更难一点:如果我需要围绕我的重复生成的控制结构怎么办?
itertools 通常在那里也有帮助 - 你需要在你认为没有的地方提供具体的例子.
例如,我可能希望永远使用不同的参数调用子生成器.
itertools.chain.from_iterable.
或者我的子发电机可能非常昂贵,我只想在它们到达时启动它们.
双方chain并chain_from_iterable做到这一点-直到需要从它那一瞬间的第一项没有子迭代器"启动".
或者(这是一个真正的愿望)我可能想根据我的控制器使用send()传递给我的内容来改变我接下来做的事情.
非常感谢一个具体的例子.无论如何,最糟糕的情况是,你将编码for x in blargh: yield x暂停的Pep3080允许你编码的地方yield from blargh- 大约4个额外的字符(不是悲剧;-).
如果一些复杂的coroutine版本的一些itertools功能(itertools主要支持迭代器 - 还没有相应的coroutools模块)变得有保证,因为在你的代码中经常重复一定程度的协程组合,那么你自己编写代码就不难了.
例如,假设我们经常发现自己做的事情:首先产生一定的价值; 然后,反复地,如果我们被发送'foo',则从fooiter产生下一个项目,如果'bla',来自blaiter,如果'zop',来自zopiter,其他任何东西,来自defiter.一旦我们发现第二次出现这种组合模式,我们就可以编码:
def corou_chaiters(initsend, defiter, val2itermap):
currentiter = iter([initsend])
while True:
val = yield next(currentiter)
currentiter = val2itermap(val, defiter)
Run Code Online (Sandbox Code Playgroud)
并在需要时调用这个简单的组合函数.如果我们需要编写其他协程,而不是一般的迭代器,我们将使用send方法而不是下一个内置函数使用稍微不同的作曲家; 等等.
如果你能提供一个不容易通过这种技术驯服的例子,我建议你在一个单独的问题(专门针对类似协程的发生器)中这样做,因为这个问题已经有很多与之无关的材料了.你的另一个,更复杂/更复杂的例子.
您希望将多个迭代器链接在一起:
from itertools import chain
def sillyGenerator(a,b,c):
return chain(quadraticRange(a),quadraticRange(b),quadraticRange(c))
Run Code Online (Sandbox Code Playgroud)
不切实际(不幸的)回答:
from __future__ import PEP0380
def sillyGenerator():
yield from quadraticRange(10)
yield from quadraticRange(12)
yield from quadraticRange(8)
Run Code Online (Sandbox Code Playgroud)
潜在的实用参考:委托给子发电机的语法
不幸的是,这种做法不切实际:Python语言暂停
更新 2011年2月:
暂停已取消,PEP 380在Python 3.3的TODO列表中.希望这个答案很快就会实用.
| 归档时间: |
|
| 查看次数: |
1319 次 |
| 最近记录: |