在 Tornado 协程中使用常规 Python 生成器
Using regular Python generator in 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 中,第一个产量是常规 Python 产量。第二个产量是龙卷风的。他们可以一起工作吗?
我得到这个工作。我没有在非协程 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)
Edit 1. The original proposal using .result()
does not work. I got
"DummyFuture does not support blocking for results" when I have a
non-trivial query_children_async()
.
Python生成器协议基于同步接口; asynchronous 代码不可能像协程一样作为生成器的一部分与 for
一起使用(协程最重要的规则:任何调用协程的东西都必须也是一个协程,或者至少知道协程。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)
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 中,第一个产量是常规 Python 产量。第二个产量是龙卷风的。他们可以一起工作吗?
我得到这个工作。我没有在非协程 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)
Edit 1. The original proposal using
.result()
does not work. I got "DummyFuture does not support blocking for results" when I have a non-trivialquery_children_async()
.
Python生成器协议基于同步接口; asynchronous 代码不可能像协程一样作为生成器的一部分与 for
一起使用(协程最重要的规则:任何调用协程的东西都必须也是一个协程,或者至少知道协程。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)