yield 什么时候会在函数调用栈中实际产生?

When will yield actually yield in the function call stack?

我正在 python 3.4.3.

中研究龙卷风和电机

我得到了三个文件。让我们将其命名为 main.pymodel.pycore.py

我有三个函数,每个函数一个...

main.py

def getLoggedIn(request_handler):
    # request_handler = tornado.web.RequestHandler()
    db = request_handler.settings["db"]
    uid = request_handler.get_secure_cookie("uid")
    result = model.Session.get(db, uid=uid)
    return result.get("_id", None) if result else None

model.py

@classmethod
    def get(cls, db, user_id=None, **kwargs):
        session = core.Session(db)
        return session.get(user_id, **kwargs)

core.py

@gen.coroutine
    def get(self, user_id, **kwargs):
        params = kwargs
        if user_id:
            params.update({"_id": ObjectId(user_id)}) #This does not exist in DB
        future = self.collection.find_one(params)
        print(future) #prints <tornado.concurrent.Future object at 0x04152A90>
        result = yield future
        print(result) #prints None
        return result

调用看起来像 getLoggedIn => model.get => core.get

core.get 用 @gen.coroutine 修饰,我调用 yield self.collection.find_one(params) print(result) 打印 None 但如果我 return 结果并尝试在 getLoggedIn 函数中打印 return 值,它会打印 .

我相信这与 tornado 的异步特性有关,打印在 yield 之前被调用,但我不确定。如果有人可以解释 coroutine/generators 不同可能情况下的原则和行为,那将是一个很大的帮助。

PEP 255 涵盖了生成器的原始规范。 但是,tornado 在协程内部以一种非常特殊的方式使用 yieldhttp://www.tornadoweb.org/en/stable/guide/coroutines.html#how-it-works

您的代码看起来或闻起来都不像普通的生成器,因为 Python 生成器的概念被 tornado 用来定义协程。 我会说你并不是真的想要生成器编写的原理,而是 tornado 生成器 的原理——一个完全不同的野兽。

分配 yield 的值是包装 @gen.coroutine 装饰器将未来的结果传回 core.get 的一种方式。 这样,result 不会被分配到未来的对象,而是 future.result().

yield future 实质上是暂停您的函数并将其转换为 future 将调用的回调,在 yield 的位置恢复执行。 正如您担心的那样,tornado 的异步性质不允许 yieldprint 之前的 运行。

很可能,你的 Future 没有返回任何东西,或者正在返回 None(我知道在语义上是等价的)。 最好将 result = yield future 视为 result = future.result()

的特殊版本

每次调用协程都必须让出,调用者也必须是协程。所以 getLoggedIn 必须是调用的协程:

result = yield model.Session.get(db, uid=uid)

等等。有关详细示例和解释,请参阅我在 refactoring Tornado coroutines 上的文章。