在Tornado协程中使用常规Python生成器

时间:2015-11-02 16:14:34

标签: python generator tornado coroutine

Python生成器是一个很棒的功能。它允许我编码复杂的,可能是递归的遍历逻辑,并将其与用户分离。通常我会像下面的代码一样使用它

TREE = {
  1: [2,3],
  2: [],
  3: [4,5],
  4: [6],
  5: [],
  6: []
  }   

def query_children(node):
    return TREE[node]

def walk_tree(root):
    # recursive tree traversal logic
    yield root
    children = query_children(root)
    for child in children:
        for node in walk_tree(child):
            yield node

def do_something():
   # nice linear iterator
   for node in walk_tree(root):
       print(node)

Tornado使用生成器是实现协同程序,这也是构建异步函数而不回调的好方法。

然而,当我试图同时使用它们时我会感到困惑。

@gen.coroutine
def query_children(node):
    ...
    raise gen.Return(children)


def walk_tree(root):
    # recursive tree traversal logic
    yield root
    children = yield query_children(root)
    for child in children:
        for node in walk_tree(child):
            yield node


def do_something():
   # nice linear iterator
   for node in walk_tree(root):
       print(node)

在新的walk_tree中,第一个yield是常规的Python yield。第二个产量是龙卷风。他们可以一起工作吗?

2 个答案:

答案 0 :(得分:3)

Python生成器协议基于同步接口;不可能像协程一样使用异步代码作为与for一起使用的生成器的一部分(协同程序最重要的规则:调用协程的任何东西也必须是coroutine,或者至少知道coroutines。for语句对它们一无所知,它就是调用你的生成器的东西。相反,我建议使用tornado.queues.Queue

@gen.coroutine
def query_children(node):
    ...
    raise gen.Return(children)


def walk_tree(queue, root):
    # recursive tree traversal logic
    yield root
    children = yield query_children(root)
    for child in children:
        for node in walk_tree(child):
            yield queue.put(node)
     yield queue.put(None)


def do_something():
    queue = tornado.queues.Queue()
    IOLoop.current().spawn_callback(walk_tree, queue, root)
    while True:
        node = yield queue.get()
        if node is None:
            break
        print(node)

答案 1 :(得分:0)

我得到了这个工作。我不是在非协程walk_tree()中使用yield,而是通过调用IOLoop.run_sync来获取子节点来同步运行它。我是龙卷风新手。所以,如果这是一个合法的解决方案或者还有其他更好的方法,请评论。

TREE = {
  1: [2,3],
  2: [],
  3: [4,5],
  4: [6],
  5: [],
  6: []
  }   

@gen.coroutine
def query_children_async(node):
    raise gen.Return(TREE[node])

# this is a regular Python generator
def walk_tree(root):
    # recursive tree traversal logic
    yield root
    # call .result() of the Future
    children = IOLoop.instance().run_sync(lambda: query_children_async(root))
    for child in children:
        for node in walk_tree(child):
            yield node

@gen.coroutine
def do_something(root):
    # just collect the result
    result = [node for node in walk_tree(root)]
    raise gen.Return(result)
  

修改1.使用.result()的原始提案不起作用。我有   " DummyFuture不支持阻止结果"当我有一个   非平凡的query_children_async()