【问题标题】:When will yield actually yield in the function call stack?函数调用堆栈中的 yield 什么时候会实际产生?
【发布时间】:2015-09-02 15:16:53
【问题描述】:

我正在 python 3.4.3 中研究龙卷风和电机。

我有三个文件。让我们将其命名为main.py、model.py、core.py

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

ma​​in.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 但如果我返回结果并尝试在 getLoggedIn 函数中打印返回值,它会打印。

我相信这与龙卷风的异步性质有关,打印在 yield 之前被调用,但我不确定。如果有人可以解释协程/生成器在不同可能情况下的原理和行为,那将是一个很大的帮助。

【问题讨论】:

  • print(future) 是做什么的?
  • 我删除了我之前的评论,因为我现在看到这是你问题的一部分。你希望result = yield future 做什么?我认为这将是一个语法错误。这并不让我感到惊讶;它给None 一点也不让我吃惊。 yield 的使用与return 大致相似,但设置了一个生成器并保存了函数状态。
  • print(future) 打印 Future 对象并且 print(result) 打印 None
  • 我希望结果是一个生成器对象,而不是未来。 core.get() 返回的 result 在我打印时被 model.get() 作为 Future 对象接收。

标签: python tornado tornado-motor


【解决方案1】:

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

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

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

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

很可能,您的 Future 没有返回任何内容,或者正在返回 None(我知道,语义等价)。 最好将result = yield future 视为result = future.result() 的专用版本

【讨论】:

  • 该集合没有文档,它应该返回None,这很好。我无法理解的是getLoggedIn() 中的result 将对象作为Future。不应该是None
  • 我能想到的唯一与此一致的解释是model.Session.get 正在返回一个Future。通过打印,您有效地验证了core.get 返回None,而不是Future。
【解决方案2】:

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

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

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

【讨论】:

  • 不仅仅是答案,我很高兴在这里见到你;)。我从您网站上的视频教程开始了 Motor + Tornado。在我接受这个答案之前给我时间:)
猜你喜欢
  • 1970-01-01
  • 2014-11-24
  • 2020-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-22
  • 2015-08-08
相关资源
最近更新 更多