1. yield的基本概念
2. Future的用法
3. ioloop的常用接口
4. gen.coroutine的应用
5. gen.coroutine的源码
6. Runner类的实现
1. yield的基本概念
python中使用yield实现了生成器函数,同样yield.send( )方法也实现了控制权在函数间的跳转。
关于yield比较详细的介绍,可以参考这篇博文http://www.cnblogs.com/coderzh/articles/1202040.html
2.Future的用法
Future是用来在异步过程中,存放结果的容器。它可以在结果出来时,进行回调。
add_done_callback(self, fn): 添加回调函数fn,函数fn必须以future作为参数。
set_result(self, result): 存放结果,此函数会执行回调函数。
set_exception(self, exception):存放异常,此函数会执行回调函数。
3. ioloop的常用接口
add_future(self, future, callback): 当future返回结果时,会将callback登记到ioloop中,由ioloop在下次轮询时调用。
add_callback(self, callback, *args, **kwargs): 将callback登记到ioloop中,由ioloop在下次轮询时调用。
add_timeout(self, deadline, callback, *args, **kwargs): 将callback登记到ioloop中,由ioloop在指定的deadline时间调用。
def call_at(self, when, callback, *args, **kwargs): 将callback登记到ioloop中,由ioloop在指定的when时间调用。
def call_later(self, delay, callback, *args, **kwargs): 将callback登记到ioloop中, 由ioloop在指定的延迟delay秒后调用。
add_handler(self, fd, handler, events): 将文件符fd的event事件和回调函数handler登记到ioloop中,当事件发生时,触发。
4. gen.coroutine的应用
直接上官网的例子
普通的回调函数的方式:
|
1
2
3
4
5
6
7
8
9
10
|
class
AsyncHandler(RequestHandler):
@asynchronous
def
get(
self
):
http_client
=
AsyncHTTPClient()
http_client.fetch(
"http://example.com"
,
callback
=
self
.on_fetch)
def
on_fetch(
self
, response):
do_something_with_response(response)
self
.render(
"template.html"
)
|
同步的方式:
|
1
2
3
4
5
6
7
|
class
GenAsyncHandler(RequestHandler):
@gen.coroutine
def
get(
self
):
http_client
=
AsyncHTTPClient()
response
=
yield
http_client.fetch(
"http://example.com"
)
do_something_with_response(response)
self
.render(
"template.html"
)
|
可以看出代码明显清晰,简单多了。如果有深层次的回调,效果会更明显。
5. gen.coroutine的源码
|
1
2
|
def
coroutine(func, replace_callback
=
True
):
return
_make_coroutine_wrapper(func, replace_callback
=
True
)
|
接着看_make_coroutine_wrapper的源码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
def
_make_coroutine_wrapper(func, replace_callback):
@functools.wraps(func)
def
wrapper(
*
args,
*
*
kwargs):
future
=
TracebackFuture()
if
replace_callback
and
'callback'
in
kwargs:
callback
=
kwargs.pop(
'callback'
)
IOLoop.current().add_future(
future,
lambda
future: callback(future.result()))
try
:
result
=
func(
*
args,
*
*
kwargs)
except
(Return, StopIteration) as e:
result
=
getattr
(e,
'value'
,
None
)
except
Exception:
future.set_exc_info(sys.exc_info())
return
future
else
:
if
isinstance
(result, types.GeneratorType):
# Inline the first iteration of Runner.run. This lets us
# avoid the cost of creating a Runner when the coroutine
# never actually yields, which in turn allows us to
# use "optional" coroutines in critical path code without
# performance penalty for the synchronous case.
try
:
orig_stack_contexts
=
stack_context._state.contexts
yielded
=
next
(result)
if
stack_context._state.contexts
is
not
orig_stack_contexts:
yielded
=
TracebackFuture()
yielded.set_exception(
stack_context.StackContextInconsistentError(
'stack_context inconsistency (probably caused '
'by yield within a "with StackContext" block)'
))
except
(StopIteration, Return) as e:
future.set_result(
getattr
(e,
'value'
,
None
))
except
Exception:
future.set_exc_info(sys.exc_info())
else
:
Runner(result, future, yielded)
try
:
return
future
finally
:
future
=
None
future.set_result(result)
return
future
return
wrapper
|
这段代码注意到,有几个关键地方。
|
1
|
future
=
TracebackFuture()
|
TracebackFuture就是Future,两者是同一个类。这里定义了future变量, 是用来后面增加callback用的。当函数执行完时,会将这个future返回。
|
1
|
result
=
func(
*
args,
*
*
kwargs)
|
这里执行被装饰的函数,返回result。我们知道有yield语句的函数,返回结果是一个生成器。
|
1
|
yielded
=
next
(result)
|
返回yield结果, next(result)被要求返回Future,list, dict,或者YieldPoint类型,一般返回Future。
|
1
|
Runner(result, future, yielded)
|
实例化Runner,这步很重要。
|
1
|
return
future
|
返回future。
6. Runner类的实现
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class
Runner(
object
):
def
__init__(
self
, gen, result_future, first_yielded):
self
.gen
=
gen
self
.result_future
=
result_future
self
.future
=
_null_future
self
.yield_point
=
None
self
.pending_callbacks
=
None
self
.results
=
None
self
.running
=
False
self
.finished
=
False
self
.had_exception
=
False
self
.io_loop
=
IOLoop.current()
self
.stack_context_deactivate
=
None
if
self
.handle_yield(first_yielded):
self
.run()
|
__init__构造函数首先初始化参数,然后调用handle_yield方法。
因为first_yielded为Future类型, 所以简化下handle_yield的代码。
|
1
2
3
4
5
6
7
|
def
handle_yield(
self
, yielded):
self
.future
=
yielded
if
not
self
.future.done():
self
.io_loop.add_future(
self
.future,
lambda
f:
self
.run())
return
False
return
True
|
handle_yield方法,首先判断yielded是否已经返回结果,如果没有就调用io_loop.add_future方法。这样当yielded返回结果时,就会回调self.run方法。如果已经返回,就直接执行self.run方法。
self.run方法会将控制权返回给被gen.coroutine的函数。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
def
run(
self
):
"""Starts or resumes the generator, running until it reaches a
yield point that is not ready.
"""
if
self
.running
or
self
.finished:
return
try
:
self
.running
=
True
while
True
:
future
=
self
.future
if
not
future.done():
return
self
.future
=
None
try
:
orig_stack_contexts
=
stack_context._state.contexts
try
:
value
=
future.result()
except
Exception:
self
.had_exception
=
True
yielded
=
self
.gen.throw(
*
sys.exc_info())
else
:
yielded
=
self
.gen.send(value)
if
stack_context._state.contexts
is
not
orig_stack_contexts:
self
.gen.throw(
stack_context.StackContextInconsistentError(
'stack_context inconsistency (probably caused '
'by yield within a "with StackContext" block)'
))
except
(StopIteration, Return) as e:
self
.finished
=
True
self
.future
=
_null_future
if
self
.pending_callbacks
and
not
self
.had_exception:
# If we ran cleanly without waiting on all callbacks
# raise an error (really more of a warning). If we
# had an exception then some callbacks may have been
# orphaned, so skip the check in that case.
raise
LeakedCallbackError(
"finished without waiting for callbacks %r"
%
self
.pending_callbacks)
self
.result_future.set_result(
getattr
(e,
'value'
,
None
))
self
.result_future
=
None
self
._deactivate_stack_context()
return
except
Exception:
self
.finished
=
True
self
.future
=
_null_future
self
.result_future.set_exc_info(sys.exc_info())
self
.result_future
=
None
self
._deactivate_stack_context()
return
if
not
self
.handle_yield(yielded):
return
finally
:
self
.running
=
False
|
这里注意到几个关键地方。
|
1
|
value
=
future.result()
|
获取yielded的结果值。
|
1
|
yielded
=
self
.gen.send(value)
|
使用send方法将结果返回,并且将执行权交给被装饰的函数。并且返回下一个yield的值。
如果函数生成器后面没有yield语句了,就会抛出StopIteration异常。
调用set_result存储结果,返回。
|
1
|
self
.result_future.set_result(
getattr
(e,
'value'
,
None
))
|
如果函数生成器后面还有yield语句,就会调用handle_yield,仍然登记self.run方法。
|
1
2
|
if
not
self
.handle_yield(yielded):
return
|
之所以要有个判断,是需要在while循环里,如果future已经有返回结果了,就继续取结果,send结果,
一直到future的结果没有返回或者没有yield语句了。
本文深入探讨了Tornado框架中的Coroutine装饰器如何实现回调函数的同步方式,极大提升代码可读性。通过yield、Future、ioloop等关键模块的运用,实现了异步操作的高效管理。详细解析了Coroutine装饰器的工作流程,包括yield的基本概念、Future的用法、ioloop的常用接口以及gen.coroutine的应用与源码实现,同时展示了如何在实际代码中应用Coroutine装饰器以简化回调函数的编写。
777

被折叠的 条评论
为什么被折叠?



