Joe*_*Joe 9 python asynchronous functional-programming generator tornado
我是Python和函数式编程的新手.我使用的是2.7.6版本
我正在使用Tornado框架来发出异步网络请求.根据我对函数式编程的了解,我希望我的数据通过使用生成器来传输我的代码.我使用生成器完成了我需要的大部分工作,并在通过函数调用流转换数据时进行转换.
在我的流的最后,我想为一些数据发出REST请求.在我将数据提交给Tornado之前,我有一个for-loop,启动pull,然后发送http请求.Tornado提供的http对象将回调函数作为一个选项,并且总是返回一个Future--实际上是一个Tornado Future对象,而不是官方的Python Future.
我的问题是,由于我现在使用生成器通过我的代码提取数据,我不再想使用回调函数.我的理由是,在我从回调中获取数据之后,我的数据现在被推送到我的代码中,我再也无法使用生成器了.
我的目标是创建一个如下所示的界面:
urls = (...generated urls...)
responses = fetch(urls)
Run Code Online (Sandbox Code Playgroud)
响应是完成的URL上的生成器.
我尝试做的事情 - 在许多方面 - 是将回调的结果转换为生成器.我正在考虑这样的事情,尽管我很快就会解决其他问题,我很快就会解释.但是,我希望我的fetch函数看起来像这样:
def fetch(urls):
def url_generator():
while True:
val = yield
yield val
@curry
def handler(gen, response):
gen.send(response)
gen = url_generator()
for u in urls:
http.fetch(u, callback=handler(gen))
return gen
Run Code Online (Sandbox Code Playgroud)
我简化了代码和语法以专注于问题,但我认为这样做会很好.我的策略是定义一个协程/生成器,然后我会在收到它们时发送响应.
我最麻烦的是协程/发电机.即使我以上述方式定义一个生成器并执行以下操作,我也会得到一个无限循环 - 这是我的主要问题之一.
def gen():
while True:
val = yield
print 'val', val
yield val
print 'after', val
break
g = gen()
g.send(None)
g.send(10)
for e in g:
print e
Run Code Online (Sandbox Code Playgroud)
这打印val 10 after 10
在协程中,正如预期的那样打破,但是for循环永远不会得到10的值.当中断时它不打印任何东西.如果我删除了中断,那么我得到无限循环:
val None
None
after None
None
val None
None
after None
None
...
Run Code Online (Sandbox Code Playgroud)
如果我删除for循环,那么协程只会val 10
在第二次产量等待时打印.我期待这个.但是,使用它不会产生任何效果.
类似地,如果我删除for循环并替换它print next(g)
,那么我得到一个StopIteration错误,我假设这意味着我在没有更多值的生成器上调用了下一个.
无论如何,当我深入研究Python时,我完全失去了.我认为这是Python中常见的情况,有人知道一个很好的方法.我搜索'转换回发电机'等等,但没有太多运气.
另一方面,我可能会从http请求中获得每个未来,但我没有太多运气"等待"未来完成的收益率.我读了很多关于'yield from'的内容,但它似乎是Python 3特有的,而Tornado似乎还没有在Python 3上运行.
感谢您查看,感谢您提供的任何帮助.
Tornado 在 Python 3 上运行得很好。
上面简化代码的问题是这没有达到您的预期:
val = yield
Run Code Online (Sandbox Code Playgroud)
您希望生成器在那里暂停(阻止您的 for 循环)直到其他函数调用g.send(value)
,但事实并非如此。相反,代码的行为如下:
val = yield None
Run Code Online (Sandbox Code Playgroud)
因此,for 循环接收None
值的速度与处理它们的速度一样快。它接收到每个后None
,会隐式调用g.next()
,这与g.send(None)
. 因此,您的代码相当于:
def gen():
while True:
val = yield None
print 'val', val
yield val
print 'after', val
g = gen()
g.send(None)
g.send(10)
while True:
try:
e = g.send(None)
print e
except StopIteration:
break
Run Code Online (Sandbox Code Playgroud)
阅读这个版本的代码,其中隐式行为变得显式,我希望清楚为什么它只是None
在无限循环中生成。
您需要的是某种方法,让一个函数将项目添加到队列的头部,而另一个函数则阻止等待项目,并在它们准备好时将它们从队列的尾部拉出。从 Tornado 4.2 开始,我们就做到了:
http://www.tornadoweb.org/en/stable/queues.html
网络蜘蛛示例与您想要做的很接近,我相信您可以调整它:
http://www.tornadoweb.org/en/stable/guide/queues.html