yield 什么时候会在函数调用栈中实际产生?
When will yield actually yield in the function call stack?
我正在 python 3.4.3.
中研究龙卷风和电机
我得到了三个文件。让我们将其命名为 main.py
、model.py
、core.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
在协程内部以一种非常特殊的方式使用 yield
:http://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
的异步性质不允许 yield
到 print
之前的 运行。
很可能,你的 Future 没有返回任何东西,或者正在返回 None
(我知道在语义上是等价的)。
最好将 result = yield future
视为 result = future.result()
的特殊版本
每次调用协程都必须让出,调用者也必须是协程。所以 getLoggedIn 必须是调用的协程:
result = yield model.Session.get(db, uid=uid)
等等。有关详细示例和解释,请参阅我在 refactoring Tornado coroutines 上的文章。
我正在 python 3.4.3.
中研究龙卷风和电机我得到了三个文件。让我们将其命名为 main.py
、model.py
、core.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
在协程内部以一种非常特殊的方式使用 yield
:http://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
的异步性质不允许 yield
到 print
之前的 运行。
很可能,你的 Future 没有返回任何东西,或者正在返回 None
(我知道在语义上是等价的)。
最好将 result = yield future
视为 result = future.result()
每次调用协程都必须让出,调用者也必须是协程。所以 getLoggedIn 必须是调用的协程:
result = yield model.Session.get(db, uid=uid)
等等。有关详细示例和解释,请参阅我在 refactoring Tornado coroutines 上的文章。