Python中的协程大概经历了以下三个阶段:
1. 最初的生成器变形yield/send
2. 引入@asyncio.coroutine和yield from
3. 在最近的Python3.5版本中引入async/await关键字app
def mygen(alist): while len(alist) > 0: c = randint(0, len(alist)-1) yield alist.pop(c) a = ["aa","bb","cc"] c=mygen(a) print(c) 输出:<generator object mygen at 0x02E5BF00>
像上面代码中的c就是一个生成器。生成器就是一种迭代器,可使用for进行迭代。生成器函数最大的特色是能够接受外部传入的一个变量,并根据变量内容计算结果后返回。
这一切都是靠生成器内部的send()函数实现的。dom
def gen(): value=0 while True: receive=yield value if receive=='e': break value = 'got: %s' % receive g=gen() print(g.send(None)) print(g.send('hello')) print(g.send(123456)) print(g.send('e'))
上面生成器函数中最关键也是最易理解错的,就是receive=yield value这句,若是对循环体的执行步骤理解错误,就会失之毫厘,差之千里。 异步
其实receive=yield value包含了3个步骤: async
一、向函数外抛出(返回)value 函数
二、暂停(pause),等待next()或send()恢复 oop
三、赋值receive=MockGetValue() 。 这个MockGetValue()是假想函数,用来接收send()发送进来的值spa
执行流程: .net
一、经过g.send(None)或者next(g)启动生成器函数,并执行到第一个yield语句结束的位置。这里是关键,不少人就是在这里搞糊涂的。运行receive=yield value语句时,咱们按照开始说的拆开来看,实际程序只执行了1,2两步,程序返回了value值,并暂停(pause),并无执行第3步给receive赋值。所以yield value会输出初始值0。这里要特别注意:在启动生成器函数时只能send(None),若是试图输入其它的值都会获得错误提示信息。线程
二、经过g.send('hello'),会传入hello,从上次暂停的位置继续执行,那么就是运行第3步,赋值给receive。而后计算出value的值,并回到while头部,遇到yield value,程序再次执行了1,2两步,程序返回了value值,并暂停(pause)。此时yield value会输出”got: hello”,并等待send()激活。设计
三、经过g.send(123456),会重复第2步,最后输出结果为”got: 123456″。
四、当咱们g.send(‘e’)时,程序会执行break而后推出循环,最后整个函数执行完毕,因此会获得StopIteration异常。
从上面能够看出, 在第一次send(None)启动生成器(执行1–>2,一般第一次返回的值没有什么用)以后,对于外部的每一次send(),生成器的实际在循环中的运行顺序是3–>1–>2,也就是先获取值,而后dosomething,而后返回一个值,再暂停等待。
看一段代码:
def g1(): yield range(5) def g2(): yield from range(5) it1 = g1() it2 = g2() for x in it1: print(x) for x in it2: print(x)
输出:
range(0, 5)
0
1
2
3
4
这说明yield就是将range这个可迭代对象直接返回了。
而yield from解析了range对象,将其中每个item返回了。
yield from iterable本质上等于for item in iterable: yield item的缩写版
来看一下例子,假设咱们已经编写好一个斐波那契数列函数
def fab(max): n,a,b = 0,0,1 while n < max: yield b # print b a, b = b, a + b n = n + 1 f=fab(5)
fab不是一个普通函数,而是一个生成器。所以fab(5)并无执行函数,而是返回一个生成器对象(生成器必定是迭代器iterator,迭代器必定是可迭代对象iterable)
如今咱们来看一下,假设要在fab()的基础上实现一个函数,调用起始都要记录日志
def f_wrapper(fun_iterable): print('start') for item in fun_iterable: yield item print('end') wrap = f_wrapper(fab(5)) for i in wrap: print(i,end=' ')
如今使用yield from代替for循环
import logging def f_wrapper2(fun_iterable): print('start') yield from fun_iterable #注意此处必须是一个可生成对象 print('end') wrap = f_wrapper2(fab(5)) for i in wrap: print(i,end=' ')
再强调一遍:yield from后面必须跟iterable对象(能够是生成器,迭代器)
yield from在asyncio模块中得以发扬光大。以前都是咱们手工切换协程,如今当声明函数为协程后,咱们经过事件循环来调度协程。
先看示例代码:
import asyncio,random @asyncio.coroutine def smart_fib(n): index = 0 a = 0 b = 1 while index < n: sleep_secs = random.uniform(0, 0.2) yield from asyncio.sleep(sleep_secs) #一般yield from后都是接的耗时操做 print('Smart one think {} secs to get {}'.format(sleep_secs, b)) a, b = b, a + b index += 1 @asyncio.coroutine def stupid_fib(n): index = 0 a = 0 b = 1 while index < n: sleep_secs = random.uniform(0, 0.4) yield from asyncio.sleep(sleep_secs) #一般yield from后都是接的耗时操做 print('Stupid one think {} secs to get {}'.format(sleep_secs, b)) a, b = b, a + b index += 1 if __name__ == '__main__': loop = asyncio.get_event_loop() tasks = [ smart_fib(10), stupid_fib(10), ] loop.run_until_complete(asyncio.wait(tasks)) print('All fib finished.') loop.close()
yield from语法可让咱们方便地调用另外一个generator。
本例中yield from后面接的asyncio.sleep()是一个coroutine(里面也用了yield from),因此线程不会等待asyncio.sleep(),而是直接中断并执行下一个消息循环。当asyncio.sleep()返回时,线程就能够从yield from拿到返回值(此处是None),而后接着执行下一行语句。
asyncio是一个基于事件循环的实现异步I/O的模块。经过yield from,咱们能够将协程asyncio.sleep的控制权交给事件循环,而后挂起当前协程;以后,由事件循环决定什么时候唤醒asyncio.sleep,接着向后执行代码。
协程之间的调度都是由事件循环决定。
yield from asyncio.sleep(sleep_secs) 这里不能用time.sleep(1)由于time.sleep()返回的是None,它不是iterable,还记得前面说的yield from后面必须跟iterable对象(能够是生成器,迭代器)。
因此会报错:
yield from time.sleep(sleep_secs)
TypeError: ‘NoneType’ object is not iterable
弄清楚了asyncio.coroutine和yield from以后,在Python3.5中引入的async和await就不难理解了:能够将他们理解成asyncio.coroutine/yield from的完美替身。固然,从Python设计的角度来讲,async/await让协程表面上独立于生成器而存在,将细节都隐藏于asyncio模块之下,语法更清晰明了。
加入新的关键字 async ,能够将任何一个普通函数变成协程
import time,asyncio,random async def mygen(alist): while len(alist) > 0: c = randint(0, len(alist)-1) print(alist.pop(c)) a = ["aa","bb","cc"] c=mygen(a) print(c) 输出: <coroutine object mygen at 0x02C6BED0>
在上面程序中,咱们在前面加上async,该函数就变成一个协程了。
可是async对生成器是无效的。async没法将一个生成器转换成协程。
仍是刚才那段代码,咱们把print改为yield
async def mygen(alist): while len(alist) > 0: c = randint(0, len(alist)-1) yield alist.pop(c) a = ["ss","dd","gg"] c=mygen(a) print(c)
能够看到输出
<async_generator object mygen at 0x02AA7170>
并非coroutine 协程对象
因此咱们的协程代码应该是这样的
import time,asyncio,random async def mygen(alist): while len(alist) > 0: c = random.randint(0, len(alist)-1) print(alist.pop(c)) await asyncio.sleep(1) strlist = ["ss","dd","gg"] intlist=[1,2,5,6] c1=mygen(strlist) c2=mygen(intlist) print(c1)
要运行协程,要用事件循环
在上面的代码下面加上:
if __name__ == '__main__': loop = asyncio.get_event_loop() tasks = [ c1, c2 ] loop.run_until_complete(asyncio.wait(tasks)) print('All fib finished.') loop.close()
原文出处:
---------------------
做者:唐大麦
来源:CSDN
原文:https://blog.csdn.net/soonfly/article/details/78361819
版权声明:本文为博主原创文章,转载请附上博文连接!
Python中的协程大概经历了以下三个阶段: 1. 最初的生成器变形yield/send 2. 引入@asyncio.coroutine和yield from 3. 在最近的Python3.5版本中引入async/await关键字1、生成器变形yield/send普通函数中若是出现了yield关键字,那么该函数就再也不是普通函数,而是一个生成器。def mygen(alist): while len(alist) > 0: c = randint(0, len(alist)-1) yield alist.pop(c)a = ["aa","bb","cc"]c=mygen(a)print(c)输出:<generator object mygen at 0x02E5BF00>123456789像上面代码中的c就是一个生成器。生成器就是一种迭代器,可使用for进行迭代。生成器函数最大的特色是能够接受外部传入的一个变量,并根据变量内容计算结果后返回。 这一切都是靠生成器内部的send()函数实现的。def gen(): value=0 while True: receive=yield value if receive=='e': break value = 'got: %s' % receiveg=gen()print(g.send(None)) print(g.send('hello'))print(g.send(123456))print(g.send('e'))12345678910111213上面生成器函数中最关键也是最易理解错的,就是receive=yield value这句,若是对循环体的执行步骤理解错误,就会失之毫厘,差之千里。 其实receive=yield value包含了3个步骤: 一、向函数外抛出(返回)value 二、暂停(pause),等待next()或send()恢复 三、赋值receive=MockGetValue() 。 这个MockGetValue()是假想函数,用来接收send()发送进来的值执行流程: 一、经过g.send(None)或者next(g)启动生成器函数,并执行到第一个yield语句结束的位置。这里是关键,不少人就是在这里搞糊涂的。运行receive=yield value语句时,咱们按照开始说的拆开来看,实际程序只执行了1,2两步,程序返回了value值,并暂停(pause),并无执行第3步给receive赋值。所以yield value会输出初始值0。这里要特别注意:在启动生成器函数时只能send(None),若是试图输入其它的值都会获得错误提示信息。二、经过g.send('hello'),会传入hello,从上次暂停的位置继续执行,那么就是运行第3步,赋值给receive。而后计算出value的值,并回到while头部,遇到yield value,程序再次执行了1,2两步,程序返回了value值,并暂停(pause)。此时yield value会输出”got: hello”,并等待send()激活。三、经过g.send(123456),会重复第2步,最后输出结果为”got: 123456″。四、当咱们g.send(‘e’)时,程序会执行break而后推出循环,最后整个函数执行完毕,因此会获得StopIteration异常。从上面能够看出, 在第一次send(None)启动生成器(执行1–>2,一般第一次返回的值没有什么用)以后,对于外部的每一次send(),生成器的实际在循环中的运行顺序是3–>1–>2,也就是先获取值,而后dosomething,而后返回一个值,再暂停等待。2、yield from看一段代码:def g1(): yield range(5)def g2(): yield from range(5)it1 = g1()it2 = g2()for x in it1: print(x)for x in it2: print(x)123456789101112输出: range(0, 5) 0 1 2 3 4这说明yield就是将range这个可迭代对象直接返回了。 而yield from解析了range对象,将其中每个item返回了。 yield from iterable本质上等于for item in iterable: yield item的缩写版 来看一下例子,假设咱们已经编写好一个斐波那契数列函数def fab(max): n,a,b = 0,0,1 while n < max: yield b # print b a, b = b, a + b n = n + 1f=fab(5) 12345678fab不是一个普通函数,而是一个生成器。所以fab(5)并无执行函数,而是返回一个生成器对象(生成器必定是迭代器iterator,迭代器必定是可迭代对象iterable) 如今咱们来看一下,假设要在fab()的基础上实现一个函数,调用起始都要记录日志def f_wrapper(fun_iterable): print('start') for item in fun_iterable: yield item print('end')wrap = f_wrapper(fab(5))for i in wrap: print(i,end=' ')12345678如今使用yield from代替for循环import loggingdef f_wrapper2(fun_iterable): print('start') yield from fun_iterable #注意此处必须是一个可生成对象 print('end')wrap = f_wrapper2(fab(5))for i in wrap: print(i,end=' ')12345678再强调一遍:yield from后面必须跟iterable对象(能够是生成器,迭代器)3、asyncio.coroutine和yield fromyield from在asyncio模块中得以发扬光大。以前都是咱们手工切换协程,如今当声明函数为协程后,咱们经过事件循环来调度协程。先看示例代码:import asyncio,random@asyncio.coroutinedef smart_fib(n): index = 0 a = 0 b = 1 while index < n: sleep_secs = random.uniform(0, 0.2) yield from asyncio.sleep(sleep_secs) #一般yield from后都是接的耗时操做 print('Smart one think {} secs to get {}'.format(sleep_secs, b)) a, b = b, a + b index += 1@asyncio.coroutinedef stupid_fib(n): index = 0 a = 0 b = 1 while index < n: sleep_secs = random.uniform(0, 0.4) yield from asyncio.sleep(sleep_secs) #一般yield from后都是接的耗时操做 print('Stupid one think {} secs to get {}'.format(sleep_secs, b)) a, b = b, a + b index += 1if __name__ == '__main__': loop = asyncio.get_event_loop() tasks = [ smart_fib(10), stupid_fib(10), ] loop.run_until_complete(asyncio.wait(tasks)) print('All fib finished.') loop.close()12345678910111213141516171819202122232425262728293031323334yield from语法可让咱们方便地调用另外一个generator。 本例中yield from后面接的asyncio.sleep()是一个coroutine(里面也用了yield from),因此线程不会等待asyncio.sleep(),而是直接中断并执行下一个消息循环。当asyncio.sleep()返回时,线程就能够从yield from拿到返回值(此处是None),而后接着执行下一行语句。 asyncio是一个基于事件循环的实现异步I/O的模块。经过yield from,咱们能够将协程asyncio.sleep的控制权交给事件循环,而后挂起当前协程;以后,由事件循环决定什么时候唤醒asyncio.sleep,接着向后执行代码。 协程之间的调度都是由事件循环决定。 yield from asyncio.sleep(sleep_secs) 这里不能用time.sleep(1)由于time.sleep()返回的是None,它不是iterable,还记得前面说的yield from后面必须跟iterable对象(能够是生成器,迭代器)。 因此会报错:yield from time.sleep(sleep_secs) TypeError: ‘NoneType’ object is not iterable4、async和await弄清楚了asyncio.coroutine和yield from以后,在Python3.5中引入的async和await就不难理解了:能够将他们理解成asyncio.coroutine/yield from的完美替身。固然,从Python设计的角度来讲,async/await让协程表面上独立于生成器而存在,将细节都隐藏于asyncio模块之下,语法更清晰明了。 加入新的关键字 async ,能够将任何一个普通函数变成协程import time,asyncio,randomasync def mygen(alist): while len(alist) > 0: c = randint(0, len(alist)-1) print(alist.pop(c))a = ["aa","bb","cc"]c=mygen(a)print(c)输出:<coroutine object mygen at 0x02C6BED0>12345678910在上面程序中,咱们在前面加上async,该函数就变成一个协程了。可是async对生成器是无效的。async没法将一个生成器转换成协程。 仍是刚才那段代码,咱们把print改为yieldasync def mygen(alist): while len(alist) > 0: c = randint(0, len(alist)-1) yield alist.pop(c)a = ["ss","dd","gg"]c=mygen(a)print(c)12345678能够看到输出<async_generator object mygen at 0x02AA7170>1并非coroutine 协程对象因此咱们的协程代码应该是这样的import time,asyncio,randomasync def mygen(alist): while len(alist) > 0: c = random.randint(0, len(alist)-1) print(alist.pop(c)) await asyncio.sleep(1) strlist = ["ss","dd","gg"]intlist=[1,2,5,6]c1=mygen(strlist)c2=mygen(intlist)print(c1)1234567891011要运行协程,要用事件循环 在上面的代码下面加上:if __name__ == '__main__': loop = asyncio.get_event_loop() tasks = [ c1, c2 ] loop.run_until_complete(asyncio.wait(tasks)) print('All fib finished.') loop.close()123456789就能够看到交替执行的效果。--------------------- 做者:唐大麦 来源:CSDN 原文:https://blog.csdn.net/soonfly/article/details/78361819 版权声明:本文为博主原创文章,转载请附上博文连接!