免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
tornado.gen 模塊解析

引言

注:正文中引用的 Tornado 代碼除特別說明外,都默認(rèn)引用自 Tornado 4.0.1。

tornado.gen 模塊是一個基于 python generator 實(shí)現(xiàn)的異步編程接口。通過該模塊提供的 coroutine (注:這里 coroutine 指的是 ”協(xié)程” 概念而不是后面具體實(shí)現(xiàn)的 decorator:@gen.decorator),大大簡化了在 Tornado 中編寫異步代碼的工作 —— 支持 “同步方式編寫異步代碼” ,避免編寫煩人的回調(diào)函數(shù)。參考官方文檔的例子,通常我們編寫的異步代碼如下:

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")

而使用 tornado.gen 模塊提供的 decorator ,在 Tornado 3.1 以前我們可以這樣寫異步代碼:

1
2
3
4
5
6
7
8
class GenAsyncHandler(RequestHandler):
@asynchronous
@gen.engine
def get(self):
http_client = AsyncHTTPClient()
response = yield http_client.fetch("http://example.com")
do_something_with_response(response)
self.render("template.html")

Tornado 3.1 及以上版本,可以直接使用 @gen.coroutine 來代替 @asynchronous:

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")

注:@asynchronous 在 tornado.web 中定義,對于使用了 @gen.coroutine 裝飾的方法不需要再使用 @asynchronous 進(jìn)行裝飾,但同時(shí)使用前述 2 個 decorator 進(jìn)行方法裝飾也是合法的,在同時(shí)使用的情況下需要注意的是 @asynchronous 必須是第 1 個 decorator 。

很顯然,采用同步方式編寫的異步代碼相比起分散在各處的異步回調(diào)函數(shù)代碼,更利于代碼的閱讀和邏輯的組織。

該模塊的實(shí)現(xiàn)非常巧妙也不容易理解,作為閱讀 Tonardo 源碼的筆記,我將在后面內(nèi)容中結(jié)合源碼和自己的理解對其實(shí)現(xiàn)進(jìn)行分析。

@gen.coroutine 與 @gen.engine 的實(shí)現(xiàn)原理

tornado.gen 支持以同步方式編寫異步代碼的核心就是 python generator。其原理簡單來說,就是通過 generator.next() 啟動 yield 返回的 generator ,通過 IOLoop 與 generator.send(value) 驅(qū)動 generator 運(yùn)行,以達(dá)到協(xié)調(diào)異步執(zhí)行的目的。

從功能上來看, @gen.coroutine 與 @gen.engine 的功能非常相似,差別就在于二者對被裝飾方法參數(shù)中的 “callback” 參數(shù)處理不一樣以及具有不同的返回值。 @gen.coroutine 裝飾的方法執(zhí)行后返回 Future 對象并且會將方法參數(shù)中的 “callback” 加入到 Future 完成后的回調(diào)列表中;@gen.engine 裝飾的方法執(zhí)行后沒有返回值(注:實(shí)際上如果被裝飾方法有返回值,會拋出 ReturnValueIgnoredError 異常,詳見后面的代碼分析部分)。

所以,通過 @gen.engine 裝飾的方法沒有返回值,方法必須自己在異步調(diào)用完成后調(diào)用 “callback” 來執(zhí)行回調(diào)動作,而通過 @gen.coroutine 裝飾的方法則可以直接返回執(zhí)行結(jié)果,然后由 gen 模塊負(fù)責(zé)將結(jié)果傳遞給 “callback” 來執(zhí)行回調(diào)。

注: 從調(diào)用者的角度來看 @gen.coroutine 可以視為 @tornado.concurrent.return_future@gen.engine 的組合。

@gen.coroutine 實(shí)現(xiàn)原理

@gen.coroutine 中充分利用了 generator 的特性,下面是其實(shí)現(xiàn)代碼及分析。

1
2
3
def coroutine(func, replace_callback=True):
"""Decorator for asynchronous generators."""
return _make_coroutine_wrapper(func, replace_callback=True)

coroutine 內(nèi)部直接委托 _make_coroutine_wrapper 完成具體功能(這段代碼中 coroutine 的可選參數(shù) “replace_callback” 是沒有使用的),返回一個 Future 實(shí)例對象。

_make_coroutine_wrapper(func, replace_callback) 函數(shù)作為 @gen.coroutine 和 @gen.engine 內(nèi)部實(shí)現(xiàn),通過 replace_callback 的值來決定是否對 “callback” 方法參數(shù)進(jìn)行處理。coroutine 的實(shí)現(xiàn)中通過 replace_callback=True 調(diào)用 _make_coroutine_wrapper 函數(shù),會檢查方法參數(shù)中是否有 “callback” 參數(shù),如果有的話會將其加入到方法返回值 Future 的完成后回調(diào)列表中。如下面代碼所示:

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def _make_coroutine_wrapper(func, replace_callback):
@functools.wraps(func)
def wrapper(*args, **kwargs):
future = TracebackFuture()

# 處理 “callback”,忽略或者將其加入到 Future 的完成回調(diào)列表中。
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:
# 在 python 2 以及 python 3.3 以前,generator 中不能直接通過
# return 返回值:return 被視為 raise StopIteration(),
# return <something> 被視為raise StopIteration(<something>)。
# 在 gen 模塊中,特別定義了 Return 類型用于返回值:raise gen.Return(something>)
result = getattr(e, 'value', None)
except Exception:
# 發(fā)生異常,異常被寫入 future(將會被設(shè)置為完成狀態(tài)),結(jié)束調(diào)用,返回 future
future.set_exc_info(sys.exc_info())
return future
else:
if isinstance(result, types.GeneratorType):
# 通過檢查 result 是否為 GeneratorType 來選擇是否創(chuàng)建 coroutine ,對于
# 同步情況直接 future.set_result(result) 返回,避免創(chuàng)建 coroutine 而
# 造成的性能損失。
# 與 Tornado 4.0 之前的版本比較,這里已經(jīng)把頂層 ExceptionStackContext
# 的構(gòu)建以及 Runner.run 的功能進(jìn)行了重構(gòu),都遷移到了 Runner 實(shí)現(xiàn)中。
#
# 通過 next 啟動 generator ,啟動前記錄上下文,啟動后對上下文進(jìn)行一致性檢查。
# 若 generator 中有從 "with StackContext" 直接 “yield” 的代碼邏輯,將拋
# 出 StackContextInconsistentError 異常。
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:
# Subtle memory optimization: if next() raised an exception,
# the future's exc_info contains a traceback which
# includes this stack frame. This creates a cycle,
# which will be collected at the next full GC but has
# been shown to greatly increase memory usage of
# benchmarks (relative to the refcount-based scheme
# used in the absence of cycles). We can avoid the
# cycle by clearing the local variable after we return it.
#
# 代碼注釋中說,generator.next() 拋出異常失敗后, future 的 exc_info
# 中會包含當(dāng)前棧幀的引用,棧幀中也有對 future 的引用,這樣導(dǎo)致一個環(huán),必須
# 要在下一次 full GC 時(shí)才能回收內(nèi)存。返回 future 后將 future 設(shè)置為 None
# 可以優(yōu)化內(nèi)存。(注:需要 full GC 是與 python 的垃圾回收實(shí)現(xiàn)采用引用計(jì)數(shù)
# 為主,標(biāo)記-清除和分代機(jī)制為輔相關(guān)。python 采用引用計(jì)數(shù)來立刻釋放可以釋放
# 的內(nèi)存,然后用標(biāo)記-清除的方法來清除循環(huán)引用的不可達(dá)對象。)
future = None

# 同步情況下,不需要創(chuàng)建 coroutine,直接返回 future。
future.set_result(result)
return future
return wrapper

class Return(Exception):
def __init__(self, value=None):
super(Return, self).__init__()
self.value = value

注: 關(guān)于 CPython 的 GC 實(shí)現(xiàn),這里有一篇不錯的源碼分析文章:Python垃圾回收機(jī)制

如下面的代碼所示, IOLoop 的 add_future 方法會封裝回調(diào)方法,在 Future 完成以后會將 “callback” 加入到 IOLoop 的回調(diào)列表中以等待 IOLoop 調(diào)度執(zhí)行回調(diào)動作。

1
2
3
4
5
6
7
8
9
10
11
12
13
def add_future(self, future, callback):
"""Schedules a callback on the ``IOLoop`` when the given
`.Future` is finished.

The callback is invoked with one argument, the
`.Future`.
"""
assert is_future(future)
callback = stack_context.wrap(callback)
# 在 future 的完成回調(diào)列表中增加一個 lambda 表達(dá)式,負(fù)責(zé)在
# 將 “callback” 加入 IOLoop 調(diào)度執(zhí)行。
future.add_done_callback(
lambda future: self.add_callback(callback, future))

從上面的代碼分析中可以看到 _make_coroutine_wrapper 函數(shù)已經(jīng)完成了 coroutine 的創(chuàng)建,其代碼邏輯比較簡單,而整個 coroutine 啟動、運(yùn)行的核心功能被實(shí)現(xiàn)在 Runner 類中。 Runner 有一個 run() 方法,該方法負(fù)責(zé)啟動 coroutine,并與 IOLoop 配合驅(qū)動 YieldPoint(注:在 generator 中通過 yield 返回的實(shí)例類型,Tornado 4.0 及以后推薦使用 Futures 類型, YieldPoint 類型被放棄) 執(zhí)行直到 result_future 完成。 run() 方法的詳細(xì)代碼如下所示:

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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

# 當(dāng)前 future 沒有完成時(shí)直接返回,等待 IOLoop 在 future 完成后回調(diào)再執(zhí)行
if not future.done():
return

# 當(dāng)前 future 完成后對 coroutine 接下來運(yùn)行沒作用,立即釋放
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:
# 將 future 的結(jié)果賦值給當(dāng)前 yield 表達(dá)式,驅(qū)動 generator 繼續(xù)
# 執(zhí)行, (如果generator未結(jié)束的話)返回下一個 yield 表達(dá)式結(jié)果
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:
# generator 執(zhí)行完成,將 執(zhí)行結(jié)果賦值給 result_future,返回
self.finished = True
self.future = _null_future

# Tornado 4.0 之前使用 YieldPoint 驅(qū)動,Callback 與 Wait/WaitAll
# 協(xié)調(diào)時(shí),Callback 的回調(diào)結(jié)果需要 Runner 作為中轉(zhuǎn)站,通過
# Runner.register_callback(key) 登記 Callback ,再通過
# YieldPoint.result_callback(key) 取回“設(shè)置(回調(diào))方法”,
# 外部通過“設(shè)置(回調(diào))方法”把結(jié)果保存到 Runner.results 字典中。
# Wait/WaitAll 通過 get_result(key) 取回 結(jié)果。
# YieldFuture 的實(shí)現(xiàn)也采用了相同的實(shí)現(xiàn)方式。
# Tornado 4.0 之后使用 Future 代替 YieldPoint,這些已經(jīng)過時(shí)。
# 與 Yield 相關(guān)的代碼都是為了向后兼容。
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

# 繼續(xù)處理 yield 表達(dá)式結(jié)果
if not self.handle_yield(yielded):
return
finally:
self.running = False

def handle_yield(self, yielded):
# 為了保持向后兼容,需要對多個 YieldPonit 和 Future 的混合集合做處理。
# 對于全是 Future 的集合類型使用新的 multi_future 函數(shù)進(jìn)行封裝處理;
# 不全是的使用 Multi 類進(jìn)行封裝,對于 Future 提供了 YieldFuture 適配器類。
# 詳細(xì)的實(shí)現(xiàn)細(xì)節(jié)見 YieldFuture、Multi的實(shí)現(xiàn)代碼。
# 若需要 run() 循環(huán)立即處理該 YieldPoint(被啟動)/Future(已經(jīng)完成) 則返
# 回 True,否則返回 False。
if isinstance(yielded, list):
if all(is_future(f) for f in yielded):
yielded = multi_future(yielded)
else:
yielded = Multi(yielded)
elif isinstance(yielded, dict):
if all(is_future(f) for f in yielded.values()):
yielded = multi_future(yielded)
else:
yielded = Multi(yielded)

# 針對第一個 YieldPoint 使用一個 ExceptionStackContext 上下文來處理
# StackContexts 中沒有處理的異常,將未處理的異常記錄到 result_future 中。
# 對于 Future 對象則沒有必要, Future 提供了方法來記錄異常和異常堆棧信息,
# 在 Future 完成后通過其 result() 方法獲取結(jié)果(在 run 方法的調(diào)用)時(shí)會
# 再次拋出異常,這時(shí)可捕獲記錄到 result_future 中。
if isinstance(yielded, YieldPoint):
self.future = TracebackFuture()
def start_yield_point():
try:
yielded.start(self)
# 如果 yielded 已經(jīng)完成,則將其結(jié)果賦值給 self.future,等待 run 循環(huán)處理;
# 若未就緒,則需要通過 Runner.set_result(key, value) 來進(jìn)行賦值操作。
if yielded.is_ready():
self.future.set_result(
yielded.get_result())
else:
self.yield_point = yielded
except Exception:
self.future = TracebackFuture()
self.future.set_exc_info(sys.exc_info())
if self.stack_context_deactivate is None:
# Start a stack context if this is the first
# YieldPoint we've seen.
with stack_context.ExceptionStackContext(
self.handle_exception) as deactivate:
self.stack_context_deactivate = deactivate
def cb():
start_yield_point()
self.run()
# 第 1 個 yielded 交由 IOLoop來啟動
self.io_loop.add_callback(cb)
return False
else:
# 啟動 YieldPoint,需要返回 True,在 run 循環(huán)中繼續(xù)處理
start_yield_point()
elif is_future(yielded):
self.future = yielded
# self.future 完成后繼續(xù) self.run()
# moment = Future() 是一個特殊的對象,主要用在需要長時(shí)間執(zhí)行的 coroutine 中,
# 通過 “yield gen.moment” 中斷當(dāng)前 coroutine ,將控制權(quán)交給 IOLoop 去輪詢。
# 等效于當(dāng)前 coroutine 臨時(shí)放棄時(shí)間片,給了其他 callback 機(jī)會運(yùn)行。
if not self.future.done() or self.future is moment:
self.io_loop.add_future(
self.future, lambda f: self.run())
return False
else:
self.future = TracebackFuture()
self.future.set_exception(BadYieldError(
"yielded unknown object %r" % (yielded,)))
return True

如上面代碼所示, Runner 的核心就是 run/handle_yield 方法,該方法的調(diào)用目前已經(jīng)被內(nèi)聯(lián)到 Runner 的初始化方法中。如下面代碼所示,根據(jù) handle_yield() 方法返回的結(jié)果立即運(yùn)行 run() 方法或者等待 IOLoop 調(diào)度運(yùn)行 run()方法(Falsehandle_yield 返回 False 時(shí),run() 方法被注冊到 IOLoop 回調(diào)中執(zhí)行。)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def __init__(self, gen, result_future, first_yielded):
self.gen = genreturn_futurereturn_future
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()
# For efficiency, we do not create a stack context until we
# reach a YieldPoint (stack contexts are required for the historical
# semantics of YieldPoints, but not for Futures). When we have
# done so, this field will be set and must be called at the end
# of the coroutine.
self.stack_context_deactivate = None
if self.handle_yield(first_yielded):
self.run()

Runner 的其他方法主要是用于支持 YieldPoint 的執(zhí)行,諸如為 YieldPoint 提供 ExceptionStackContext 支持(注:coroutine 結(jié)束時(shí)會調(diào)用 stack_context_deactivate 將該上下文設(shè)置為無效,在 stack_context._remove_deactivated 方法中會清理無效的上下文,并不會污染到上下文鏈,詳細(xì)的細(xì)節(jié)請參考 stack_context 的設(shè)計(jì)實(shí)現(xiàn))、結(jié)果中轉(zhuǎn)(注:這是個人說法,主要是指為協(xié)調(diào) Callback 與 Wait/WaitAll 提供的 register_callback、 is_ready、 set_result、 pop_result、 result_callback 幾個方法)。

@gen.engine 實(shí)現(xiàn)原理

前面內(nèi)容已經(jīng)說過 @gen.engine@gen.coroutine 是非常相似的,對使用者而言 @gen.coroutine 就是 @concurrent.return_future@gen.engine 的組合(詳見 concurrent.return_future 的實(shí)現(xiàn))。下面是 @gen.engine 的實(shí)現(xiàn)代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def engine(func):
# 不對被裝飾方法的 "callback" 參數(shù)做替換處理,也就是說即使被裝飾方法有 “callback” 參數(shù),
# 在 coroutine 執(zhí)行完成得到結(jié)果以后不會“自動調(diào)用”該 “callback”。細(xì)節(jié)將
# _make_coroutine_wrapper 實(shí)現(xiàn)代碼。
func = _make_coroutine_wrapper(func, replace_callback=False)
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 獲取 coroutine 的執(zhí)行結(jié)果,由于 coroutine 執(zhí)行完成后不會自動調(diào)用 "callback" ,所
# 以要求被裝飾方法不能有返回值(非 None)而必須自己調(diào)用 "callback",否則拋出
# ReturnValueIgnoredError 異常。
future = func(*args, **kwargs)
def final_callback(future):
if future.result() is not None:
raise ReturnValueIgnoredError(
"@gen.engine functions cannot return values: %r" %
(future.result(),))
future.add_done_callback(final_callback)
return wrapper

把普通的異步方法適配到 coroutine

通過上面的分析可以看到,Tornado 實(shí)現(xiàn)的 coroutine 支持編寫 “同步方式的異步代碼”,但是要求異步調(diào)用的返回值是 Future 或者 YieldPoint 實(shí)例,對于這樣的異步方法,我們只需要簡單的使用 yield 表達(dá)式便可以輕松將其轉(zhuǎn)換為 coroutine。而對于返回值為 None,僅支持通過 “callback” 參數(shù)回調(diào)(異步結(jié)果執(zhí)行的結(jié)果會作為 “callback” 調(diào)用的實(shí)參)的普通異步方法,便不能直接被 Tornado 的 coroutine 支持,需要我們自己做一些額外的封裝工作。

tornado.gen 模塊提供了一個標(biāo)準(zhǔn)的封裝函數(shù) Task(注:Tornado 4.0 以前 Task 是作為 YieldPoint 的子類來實(shí)現(xiàn)的,之后改為返回 Future 實(shí)例的函數(shù),為了向后兼容,所以是一個擁有“類名”的函數(shù)。)。Task 的實(shí)現(xiàn)原理很簡單,因?yàn)檫@種普通異步方法沒有返回值而是通過把異步結(jié)果作為回調(diào)函數(shù)的實(shí)參來達(dá)到傳遞的目的,所以 Task 就將這種方法包裝成返回值為 Future 的方法然后通過方法的回調(diào)函數(shù)來把異步結(jié)果傳遞給返回的 Future 實(shí)例。下面是 Task 的實(shí)現(xiàn)代碼:

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
def Task(func, *args, **kwargs):
future = Future()
def handle_exception(typ, value, tb):
if future.done():
return False
future.set_exc_info((typ, value, tb))
return True

# 提供給 func 的回調(diào)函數(shù),將 func 的異步結(jié)果傳遞給 future。
# 注意: 這個回調(diào)函數(shù)僅支持一個 “參數(shù)”。
def set_result(result):
if future.done():
return
future.set_result(result)

# 由于 func 的 “callback” 的形參個數(shù)是不確定的(或者說 func 回調(diào) “callback”
# 的形參個數(shù)不確定),要適配 set_result 就需要將形參包裝成一個對象傳遞給
# set_result。 _argument_adapter 函數(shù)就是負(fù)責(zé)完成這個封裝功能的,對于 0 個或 1
# 個實(shí)參調(diào)用的情況,_argument_adapter 不做任何處理直接將該參數(shù)傳遞給 set_result,
# 對于多個實(shí)參的情況,這將參數(shù)包裝成 Arguments(namedtuple) 傳遞給 set_result。
# 參數(shù)的 “callback”,
with stack_context.ExceptionStackContext(handle_exception):
func(*args, callback=_argument_adapter(set_result), **kwargs)
return future

Arguments = collections.namedtuple('Arguments', ['args', 'kwargs'])

def _argument_adapter(callback):
"""Returns a function that when invoked runs ``callback`` with one arg.

If the function returned by this function is called with exactly
one argument, that argument is passed to ``callback``. Otherwise
the args tuple and kwargs dict are wrapped in an `Arguments` object.
"""
def wrapper(*args, **kwargs):
if kwargs or len(args) > 1:
callback(Arguments(args, kwargs))
elif args:
callback(args[0])
else:
callback(None)
return wrapper

注:Task 內(nèi)部已經(jīng)實(shí)現(xiàn)了隱式傳遞 “callback” 參數(shù),所以使用 Task 時(shí)不能顯示傳遞 “callback”。

結(jié)束語

python generator 是個功能強(qiáng)大的利器(PEP 342 加入了新的特性,能讓生成器在單一語句中實(shí)現(xiàn),生成一個值或者接受一個值,或同時(shí)生成一個值并接受一個值。),是 tornado.gen 模塊實(shí)現(xiàn) coroutine 的基石。雖然 tornado.gen 的核心就是 generator,但是其整個設(shè)計(jì)和實(shí)現(xiàn)都非常巧妙,并且隨著 Tornado 版本的演變該模塊也在不斷重構(gòu)和優(yōu)化,對比其不同版本的實(shí)現(xiàn)演進(jìn),對于我們理解學(xué)習(xí)都非常有價(jià)值,值得反復(fù)研讀。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Python “黑魔法” 之 Generator Coroutines
簡單介紹PHP非阻塞模式
Python協(xié)程知多少
對協(xié)程的一些理解
Python 編寫ORM時(shí)的重難點(diǎn)掌握
Tornado--架構(gòu)解析(1)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服