协程的定义
-
函数
入口:有且只有一个入口
出口:有且只有一个出口 -
协程
入口:多个入口
出口:多个出口
特点:暂定,保留执行状态,恢复执行
协程是函数基础上,一种更加宽泛定义的计算机程序模块,它可以有多个入口点,允许从一个入口点到下个入口点之前暂停
,保留执行状态
,等待合适的时机恢复
执行状态,从下一个入口点重新开始性质
-
协程代码块
从一个入口点到下一个入口点的代码 -
协程模块
有n个入口代码,和n个协程代码块组成。其组织形式为:函数入口-》协程代码块-》入口点-》协程代码块...
可迭代对象,迭代器,生成器
可迭代对象(Iterable)
python中可迭代类型如字符串,列表,元组,字典等:
str = 'abc'
list = ['a', 'b', 'c']
set = ('a', 'b', 'c')
dict = {'a':1, 'b':2}
他们的特点是dir(object)
实现了__iter__()
函数
迭代器(Iterator)
迭代器的要求是dir(object)
实现了__iter__()
和__next__()
两个方法
生成器(generator)
生成器是特殊的迭代器,在迭代器的基础上包含yield
关键字
总结
- 迭代器必须实现迭代器协议
__iter__
和__next__
-
__iter__
返回的对象是可迭代对象 - 迭代器一定是可迭代对象,但可迭代对象不一定是迭代器,有可能迭代细节交给另一个类,这个类才是迭代器
- 生成器一定是一个迭代器,同时也是迭代对象
- 生成器是一种特殊的迭代器,包含关键字
yield
来实现懒惰计算,并是的外部影响生成器的执行成为可能
生成器和yield语义
生成器的定义:提供一种函数,能够返回中间结果给调用者,然后维护函数的局部状态,以便当函数离开后,也能恢复执行。
生成器是一个包含关键字yield
表达式的函数。一个生成器是异步
的,即生成器模块中含有阻塞代码,生成器的类型是types.GeneratorType
。
#生成器
def sum(total):
total = 0
while True:
a = yield total
total += a
if __name__ == '__main__':
s = sum(10) //获得生成器,但不执行生成器的代码
print next(s) //运行生成器的代码,执行yield total处,输出0
print s.send(10) //运行生成器的代码,第二次执行到yield total处,输出10
print s.send(2) //运行生成器的代码,第三次执行到yield total处,输出12
对上诉代码进行说明:
- sum是一个生成器,包含关键字
yield
- sum生成器
a=yield
从send(value)获取外部参数 - sum生成器
yield total
将total值传出 - next(s)初始化生成器,并block在第一个yield中
- s.send(v)唤醒生成器,并将值传入
Generator已经具备协程的能力,如能够暂停,保存状态,传出值;恢复执行,接受参数,异步执行。
但此时Generator还不是一个协程。一个真正的协程能够控制代码什么时候继续执行。而此时的Generator执行遇到一个yield还是把执行控制权转移给调用者
Future
Future顾名思义未来,将在未来被执行的代码。Future的类包含result,done, callback属性,以及_set_result, _set_done方法,通过这些属性和方法来实现当future被赋值后,能够执行对应的callback函数,具体代码如下:
class Future(object):
def done(self):
return self._done
def result(self, timeout=None):
self._clear_tb_log()
if self._result is not None:
return self._result
if self._exc_info is not None:
raise_exc_info(self._exc_info)
self._check_done()
return self._result
def add_done_callback(self, fn):
if self._done:
fn(self)
else:
self._callbacks.append(fn)
def set_result(self, result):
self._result = result
self._set_done()
def _set_done(self):
self._done = True
for cb in self._callbacks:
try:
cb(self)
except Exception:
app_log.exception('exception calling callback %r for %r',
cb, self)
self._callbacks = None
Future说明
1.Future对象通过add_done_callback
将回调函数与future对象进行绑定,目的是future被赋值后调用callback中的回调函数,从而实现激活生成器(回调函数调用gen.send(value)
)
2.def set_result(self, result)
:对Future对象赋值,将异常处理结果赋值给Future对象的result属性,同时执行_set_done遍历callback的函数
3.def _set_done(self)
:遍历callback的函数列表,并执行
总结来说,future对象绑定一个回调函数(回到函数会激活生成器),当future对象被赋值,会回调回调函数,进而回调函数会激活生成器(gen.send())
IOLoop类
IOLoop在协程运行环境中担任着协程调度的角色,消息循环本质上是一种事件循环,等待事件,然后运行对应事件的处理器。IOLoop主要调度处理的是IO事件(读,写,错误)以及callback和timeout。
IOLoop中注册future:add_future
def add_future(self, future, callback):
assert is_future(future)
callback = stack_context.wrap(callback)
future.add_done_callback(lambda future: self.add_callback(callback, future))
IOLoop将callback和future作为参数传入IOLoop的add_callback,封装成匿名函数
,并存储到future的callback属性中,当future被设置后匿名函数则会在下一个IOLoop
中运行
- 1.
lambda future: self.add_callback(callback, future)
,将callback,future作为参数传入IOLoop的add_callback方法,实则利用偏函数将callback和future进行封包装,包装好的偏函数加入到IOLoop的回调函数列表中。当IOLoop下一次迭代运行,遍历回调函数并执行 - 匿名函数加入到future的callback属性中,当future对象被设置后,会调用回调函数
Coroutine函数装饰器
函数装饰器本质是一个函数,它将被调用的函数func作为参数,返回一个新的函数make_coroutine_wrapper。
def coroutine(func, replace_callback=True):
return make_coroutine_wrapper(func, replace_callback=True)
image.png
如上图:
-
result = func(*arg, **kwargs)
获取函数对象,
-
-
if isinstance(result, types.GeneratorType)
判断result是否是生成器
-
-
yielded = next(result)
因为result是生成器,当调用next会执行到生成器第一个yield代码,并返回一个future对象(异步操作)赋值给yield
-
-
Runner(result, future, yielded)
,将生成器对象, yielded(future对象),future(监控future)传入函数Runner
-
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()
def run(self):
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:
try:
value = future.result()
except Exception:
self.had_exception = True
yielded = self.gen.throw(*sys.exc_info())
else:
yielded = self.gen.send(value)
except (StopIteration, Return) as e:
self.finished = True
self.future = _null_future
self.result_future.set_result(getattr(e, 'value', None))
self.result_future = None
return
except Exception:
self.finished = True
self.future = _null_future
self.result_future.set_exc_info(sys.exc_info())
self.result_future = None
return
if not self.handle_yield(yielded):
return
finally:
self.running = False
def handle_yield(self, yielded):
try:
self.future = convert_yielded(yielded)
except BadYieldError:
self.future = TracebackFuture()
self.future.set_exc_info(sys.exc_info())
if not self.future.done() or self.future is moment:
self.io_loop.add_future(
self.future, lambda f: self.run())
return False
return True
Runner函数内部处理就是将给yeilded(future对象)绑定一个回调函数,当future对象被set_result后,调用run;而run函数内部会调用生成器的send方法,并将vaule传入到生成器。
def handle_yield(self, yielded)
:
- 1.将yielded转化为future对象
- 2.调用IOLoop的
add_future
函数,将run函数添加到future的callback属性中 - 3.当future对象在某处代码中被set_result,IOLoop下一个循环中便执行run函数
- 4.run函数会取出future的result,并调用gen.send(value)启动生成器,并将生成器的输出在赋值给yielded
- 5.重新赋值的yielded在作为参数传入
handle_yield(self, yielded)
,循环步骤1,指导生成器结束
参考:
https://juejin.im/post/5ccafbf5e51d453a3a0acb42
https://blog.csdn.net/wyx819/article/details/45420017
网友评论