从Python中的协同程序中生成一个值,也称为convert callback to generator

从Python中的协同程序中生成一个值,也称为convert callback to generator,python,asynchronous,functional-programming,generator,tornado,Python,Asynchronous,Functional Programming,Generator,Tornado,我不熟悉Python和函数式编程。我使用的是2.7.6版 我正在使用Tornado框架进行异步网络请求。根据我对函数式编程的了解,我希望我的数据通过生成器在代码中流动。我已经使用生成器完成了我需要的大部分工作,并在数据流通过我的函数调用时转换数据 在流的最后,我想对一些数据发出REST请求。在我将数据提交给Tornado之前,我有一个for循环,用于启动pull,然后发送http请求。Tornado提供的http对象将回调函数作为一个选项,并始终返回一个Future——实际上是Tornado F

我不熟悉Python和函数式编程。我使用的是2.7.6版

我正在使用Tornado框架进行异步网络请求。根据我对函数式编程的了解,我希望我的数据通过生成器在代码中流动。我已经使用生成器完成了我需要的大部分工作,并在数据流通过我的函数调用时转换数据

在流的最后,我想对一些数据发出REST请求。在我将数据提交给Tornado之前,我有一个for循环,用于启动pull,然后发送http请求。Tornado提供的http对象将回调函数作为一个选项,并始终返回一个Future——实际上是Tornado Future对象,而不是官方的Python Future

我的问题是,因为我现在使用生成器在代码中提取数据,所以我不想再使用回调函数。我的理由是,在我从回调中获取数据之后,我的数据现在被推送到我的代码中,我不能再使用生成器了

我的目标是创建一个如下所示的界面:

urls = (...generated urls...)
responses = fetch(urls)
其中响应是完整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
我简化了代码和语法以关注问题,但我认为这会很好地工作。我的策略是定义一个协同程序/生成器,然后在收到响应时将其发送给它

我遇到的最大问题是协同程序/生成器。即使我用上面的方式定义了一个生成器并执行了下面的操作,我也会得到一个无限循环——这是我的主要问题之一

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
这将在corroutine中按预期在10后打印
val10,并使用中断,但for循环从未获得10的值。休息时它不会打印任何东西。如果我去掉断点,那么我得到无限循环:

val None
None
after None
None
val None
None
after None
None
...
如果我删除for循环,那么协同程序在等待第二个成品时将只打印
val10
。我希望这样。然而,使用它不会产生任何效果

类似地,如果我删除for循环并将其替换为
print next(g)
,那么我会得到一个StopIteration错误,我假设这意味着我在没有更多值的生成器上调用了next

无论如何,当我深入研究Python时,我完全不知所措。我认为这是Python中的常见情况,有人知道一种很好的方法。我搜索了“将回调转换为生成器”之类的东西,但运气不太好

另一方面,我可能会从http请求中产生每个未来,但我没有太多的运气“等待”未来完成。我读了很多关于“收益来自”的书,但它似乎是特定于Python 3的,Tornado似乎还不能在Python 3上工作


感谢您的观看,并感谢您提供的任何帮助。

Tornado在Python 3上运行得非常好

上面的简化代码的问题在于,这并没有达到预期效果:

val = yield
您希望生成器在那里暂停(阻塞for循环),直到其他函数调用
g.send(value)
,但实际情况并非如此。相反,代码的行为类似于:

val = yield None
因此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
阅读这个版本的代码,其中隐式行为是显式的,我希望能够清楚地知道为什么它只是在无限循环中生成
None

您需要的是一个函数向队列的头部添加项目,而另一个函数阻止等待项目,并在项目准备就绪时将其从队列的尾部拉出。从龙卷风4.2开始,我们有:

web spider示例接近您想要做的,我相信您可以调整它:


好问题:)。写得好,研究得好,谢谢。这是我的第一个问题:)Tornado确实与Python3一起工作,是
@curry
您的生成器吗?谢谢您的光临。我对py3上的Tornado有一些问题,但我会再次检查@curry来自PyMonad包。当我使用async generator时,我必须为特定函数预定义模式。也许你应该看看这个。我刚刚开始研究PyMonad,如果我有解决方案,我会告诉你的Jesse,非常感谢你的解释。一旦我的行为正常,我会将其标记为正确,并用有效的解决方案更新我的问题。有趣的是,在我执行此调用后,我计划将结果放入MongoDB。。。我打算用马达!事实上,我昨天在研究你的个人网站时偶然发现了它。小世界:)再次感谢你们的帮助。没问题,很高兴我能帮上一点忙。如果您还需要什么,请告诉我。