设为首页 收藏本站
查看: 530|回复: 0

[经验分享] Python高级编程之生成器(Generator)与coroutine(二):coroutine介绍

[复制链接]

尚未签到

发表于 2015-12-1 08:30:35 | 显示全部楼层 |阅读模式
原创作品,转载请注明出处:点我
  上一篇文章Python高级编程之生成器(Generator)与coroutine(一):Generator中,我们介绍了什么是Generator,以及写了几个使用Generator Function的示例,这一小节,我们会介绍Python的coroutine,以及会有一个小例子,再接下来的文章中会以代码的形式一步步介绍coroutine的高级用法。
  coroutine(协程)
  什么是coroutine?coroutine跟Generator有什么区别?下面先看一段代码:



1 def grep_co(pattern):
2     print "Lookin for %s" % pattern
3     while True:
4         # 执行完下面这句,函数挂起,等待接收数据,通过send()函数
5         # yield line
6         line = (yield )
7         if pattern in line:
8             print line
  
  grep_co就是一个coroutine,从代码的角度来看,coroutine跟generator的唯一区别就是在第6行,coroutine是



line = (yield)
  而Generator是:



yield line
  那么这两者有什么区别呢?先别急,我们接着往下看:
  在Python2.5及之后的版本中,yield可以用作为表达式,就是gerp_co()函数中的这种用法。那么问题来了,gerp_co()函数中的line的值应该是多少?这个值是谁给他的?
  答案很简单:line的值是我们(grep_co())的调用者发送过去的。怎么发送的?Easy!使用send(value)函数即可。先来看下执行效果:



1 >>> def grep_co(pattern):
2     print "Looking for %s" %pattern
3     while True:
4         line = (yield)
5         if pattern in line:
6             print line
7
8            
9 >>> g = grep_co("python")
10 >>> g
11 <generator object grep_co at 0x01FA3B98>
  跟Generator一样,当调用gerp_co("python")的时候,并不会立即执行grep_co函数,而是会返回一个generator类型的对象,同样是在这个对象调用了next()的时候才会开始执行这个函数:



>>> g.next()
Looking for python
  调用了next()函数之后,函数开始执行,执行到第6行,也就是line = (yield)这一行代码的时候,碰到了yield关键字,跟Generator一样,此时,整个函数会挂起,由于yield后面没有跟随其他的变量(一般情况下,coroutine中的yield语句也不会跟随返回值,这个后面会讲到),所以此时不会返回任何数据,只是单纯的保存执行环境并挂起暂停执行。
  既然coroutine跟Generator一样碰到yield关键字会挂起,那么是不是也跟Generator一样调用next()函数继续执行呢?其实你如果能够这样想我会很高兴,说明你有在认真的看,O(&cap;_&cap;)O~。不幸的是想法是好的,可惜是错的,O(&cap;_&cap;)O哈哈~,应该使用send(value)函数。接着上面往下走,上面调用了g.next(),函数开始执行,碰到了yield关键字,函数挂起暂停执行,现在调用g.send("I love python")函数,执行结果如下:



>>> g.send("Hello,I love python")
Hello,I love python
  可以看到,send 函数有一个参数,这个参数就是传递个line这个变量的。调用了send("I love python")这个函数之后,grep_co()这个函数会接着上次挂起的地方往下执行,也就是在第六行line = (yield)这个地方,send("I love python")函数的参数会被传递给line这个变量,然后接着往下执行,直到执行完毕或者再次碰到yield关键字。在这个例子中,line的值是"I love pyhton",pattern的值是"python",if判断为真,打印输出line,接着往下执行,因为是在一个无限循环当中,再次碰到了yield这个关键字,挂起并暂停。所以我们会看到上面的执行结果。



>>> g.send("Life is short,Please use Python")
>>> g.send("Life is short,Please use python")
Life is short,Please use python
  我们再继续调用send(value)函数,会重复上面的执行过程。
  讲了这么多,那么什么才是coroutine呢?我相信聪明的你应该已经猜到了:
  所谓的coroutine,也就是一个包含有yield关键字的函数,但是跟Generator不同的是,coroutine会以value = (yield)的方式使用yield关键字,并且接受调用者通过send(value)函数发送过来的数据,然后消费这个数据(consume the value)。
  在使用coroutine,有一点很需要注意的就是:所有的coroutine必须要先调用.next()或者send(None)才行。在调用send传入非None值前,生成器必须处于挂起状态,否则将抛出异常。当然,也可以使用.next()恢复生成器,只不过此时coroutine接收到的value为None。
  可以调用.close()关闭coroutine。关闭coroutine之后,再次调用.nect()或者.send(value)之后会抛出异常。



>>> g.close()
>>> g.send("corotuine has already closed")
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
g.send("corotuine has already closed")
StopIteration
>>>
  .close()会抛出GeneratorExit异常,我们可以在代码中捕获并处理这个异常,而且一般情况下也应该处理这个异常。



1 def grep(pattern):
2     print "Looking for %s" %pattern
3     try:
4         while True:
5             line = (yield)
6             if pattern in line:
7                 print line
8     except GeneratorExit:
9         print "Going away.Goodbye"
  当然也可以通过throw()函数在生成器内部抛出一个指定的异常。



>>> g.send("Life is short,please use python")
Life is short,please use python
>>> g.throw(RuntimeError,"You'ar hosed")
Traceback (most recent call last):
File "<pyshell#14>", line 1, in <module>
g.throw(RuntimeError,"You'ar hosed")
File "<pyshell#10>", line 5, in grep
line = (yield)
RuntimeError: You'ar hosed
  好了,coroutine已经介绍的差不多了,我们可以看到coroutine可以很方便的挂起和执行,也有多个人口点和出口点,而普通的函数一般只有一个入口点和出口点。
  Generator和coroutine用起来很像,但是仅此而已,Generator和coroutine是两个完全不相同的概念。Generator产生(返回)数据用来在迭代(iterator)中使用,而coroutine则是需要其他的地方发送数据过来,从而消费数据(consume value)。
  接下来讲的是使用coroutine要注意的地方:
  第一,就是千万别忘记了在使用coroutine前要先调用.next()函数。但是这一点经常容易忘记,所以可以使用一个function decorator.



1 # 作为装饰器用,因为经常容易会忘记调用.next()函数
2 def coroutine(func):
3     def start(*args,**kargs):
4         cr = func(*args,**kargs)
5         cr.next()
6         return cr
7     return start
  第二:最好是不要把Generator和coroutine混合在一起用,也就是receive_value = (yield return_value)这种方式来用。因为这会很难以理解,而且也会出现某些很诡异的情况。先看代码:



1 def countdown_co(n):
2     print "Counting down from ",n
3     while n >= 0:
4         newvalue = (yield n)
5         # 如果接收到了newvalue,则重新设置n
6         if newvalue is not None:
7             n = newvalue
8         else:
9             n -= 1
  代码很简单,同时使用了coroutine和Generator,诡异的情况发生了。先是写一个函数test_countdown_co():



1 def test_countdown_co():
2     c = countdown_co(5)
3     for n in c:
4         print n
5         if 5 == n:
6             c.send(3)
  然后在IDLE终端调用这个函数,可以看到函数的执行结果为:



>>> test_countdown_co()
Counting down from  5
5
2
1
0
  现在,我们在IDLE终端直接输入上面的test_countdown_co()代码来测试countdown_co()函数:



>>> c = countdown_co(5)
>>> for n in c:
print n
if 5 == n:
c.send(3)

Counting down from  5
5
3
2
1
0
>>>
  可以看到一样的代码,执行结果却不一样,好诡异啊!到现在我都没有想明白这是为什么。如果有谁知道原因,请告诉我,O(&cap;_&cap;)O谢谢!
  好了!这一篇介绍coroutine的Blog也写好了。接下来的文章会以完整的代码的形式来介绍coroutine的一些高级用法。敬请期待。O(&cap;_&cap;)O哈哈~
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-145599-1-1.html 上篇帖子: python 基础笔记 下篇帖子: Python中的装饰器
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表