原创作品,转载请注明出处:点我

上一篇文章Python高级编程之生成器(Generator)与coroutine(一):Generator中,我们介绍了什么是Generator,以及写了几个使用Generator Function的示例,这一小节,我们会介绍Python的coroutine,以及会有一个小例子,再接下来的文章中会以代码的形式一步步介绍coroutine的高级用法。

coroutine(协程)

什么是coroutine?coroutine跟Generator有什么区别?下面先看一段代码:

 def grep_co(pattern):
print "Lookin for %s" % pattern
while True:
# 执行完下面这句,函数挂起,等待接收数据,通过send()函数
# yield line
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)函数即可。先来看下执行效果:

 >>> def grep_co(pattern):
print "Looking for %s" %pattern
while True:
line = (yield)
if pattern in line:
print line >>> g = grep_co("python")
>>> g
<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(∩_∩)O~。不幸的是想法是好的,可惜是错的,O(∩_∩)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异常,我们可以在代码中捕获并处理这个异常,而且一般情况下也应该处理这个异常。

 def grep(pattern):
print "Looking for %s" %pattern
try:
while True:
line = (yield)
if pattern in line:
print line
except GeneratorExit:
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.

 # 作为装饰器用,因为经常容易会忘记调用.next()函数
def coroutine(func):
def start(*args,**kargs):
cr = func(*args,**kargs)
cr.next()
return cr
return start

第二:最好是不要把Generator和coroutine混合在一起用,也就是receive_value = (yield return_value)这种方式来用。因为这会很难以理解,而且也会出现某些很诡异的情况。先看代码:

 def countdown_co(n):
print "Counting down from ",n
while n >= 0:
newvalue = (yield n)
# 如果接收到了newvalue,则重新设置n
if newvalue is not None:
n = newvalue
else:
n -= 1

代码很简单,同时使用了coroutine和Generator,诡异的情况发生了。先是写一个函数test_countdown_co():

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

然后在IDLE终端调用这个函数,可以看到函数的执行结果为:

>>> test_countdown_co()
Counting down from 5
5
2
1

现在,我们在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(∩_∩)O谢谢!

好了!这一篇介绍coroutine的Blog也写好了。接下来的文章会以完整的代码的形式来介绍coroutine的一些高级用法。敬请期待。O(∩_∩)O哈哈~

Python高级编程之生成器(Generator)与coroutine(二):coroutine介绍的更多相关文章

  1. Python高级编程之生成器(Generator)与coroutine(一):Generator

    转载请注明出处:点我 这是一系列的文章,会从基础开始一步步的介绍Python中的Generator以及coroutine(协程)(主要是介绍coroutine),并且详细的讲述了Python中coro ...

  2. Python高级编程之生成器(Generator)与coroutine(四):一个简单的多任务系统

    啊,终于要把这一个系列写完整了,好高兴啊 在前面的三篇文章中介绍了Python的Python的Generator和coroutine(协程)相关的编程技术,接下来这篇文章会用Python的corout ...

  3. Python高级编程之生成器(Generator)与coroutine(三):coroutine与pipeline(管道)和Dataflow(数据流_

    原创作品,转载请注明出处:点我 在前两篇文章中,我们介绍了什么是Generator和coroutine,在这一篇文章中,我们会介绍coroutine在模拟pipeline(管道 )和控制Dataflo ...

  4. python高级编程技巧

    由python高级编程处学习 http://blog.sina.com.cn/s/blog_a89e19440101fb28.html Python列表解析语法[]和生成 器()语法类似 [expr  ...

  5. 第十一章:Python高级编程-协程和异步IO

    第十一章:Python高级编程-协程和异步IO Python3高级核心技术97讲 笔记 目录 第十一章:Python高级编程-协程和异步IO 11.1 并发.并行.同步.异步.阻塞.非阻塞 11.2 ...

  6. python高级编程之选择好名称:完

    由于时间关系,python高级编程不在放在这边进行学习了,如果需要的朋友可以看下面的网盘进行下载 # # -*- coding: utf-8 -*- # # python:2.x # __author ...

  7. python高级编程之列表推导式

    1. 一个简单的例子 在Python中,如果我们想修改列表中所有元素的值,可以使用 for 循环语句来实现. 例如,将一个列表中的每个元素都替换为它的平方: >>> L = [1, ...

  8. python高级编程:有用的设计模式3

    # -*- coding: utf-8 -*-__author__ = 'Administrator'#python高级编程:有用的设计模式#访问者:有助于将算法从数据结构中分离出来"&qu ...

  9. python高级编程:有用的设计模式2

    # -*- coding: utf-8 -*- __author__ = 'Administrator' #python高级编程:有用的设计模式 #代理 """ 代理对一 ...

随机推荐

  1. [Functional Programming] Use Task/Async for Asynchronous Actions

    We refactor a standard node callback style workflow into a composed task-based workflow. Original Co ...

  2. JavaScript中isPrototypeOf函数

    转自:http://www.ijavascript.cn/shouce/javascript-isprototypeof-247.html JavaScript中 isPrototypeOf 函数方法 ...

  3. Discuz常见小问题-如何删除用户

    用户-用户管理,直接拉到底部点搜索   切换到最后一页,然后勾选要删除的用户(最后一页就是最新注册的用户)   要勾选两个选项,删除过程比较长,不要中途退出,等待删除完毕   再次搜索用户就少一个了

  4. android 程序更新(没有sdcard)

    今天总结一下我们写的程序需要更新(默认用户已经安装上旧版本了),而用户更新时却没有sdcard的情况,如果有sdcard,那就不用废话了... 先说一下软件更新的基本原理,从服务器下载一个程序的apk ...

  5. 【转】使用python编写网络通信程序

    文章主体现部分来自:http://openexperience.iteye.com/blog/145701 1. 背景知识 如果使用TCP协议来传递数据,客户端和服务器端需要分别经过以下步骤: ser ...

  6. 关于图片无缝拼接的学习(PTGui)

    一.简介 在用到单反.无人机.手机等拍照工具,需要无缝拼接. 二.下载 官网:http://www.ptgui.com/download.html 其他:http://pan.baidu.com/sh ...

  7. No implementation found for long com.baidu.platform.comjni.map.commonmemcache.JNICommonMemCache.Create()

    3-21 10:14:20.833 2892-2892/? E/art: No implementation found for long com.baidu.platform.comjni.map. ...

  8. centos6.5下使用yum完美搭建LNMP环境(php5.6,mysql5.5,nginx1.10)

    准备工作 配置防火墙,开启80端口.3306端口 不用执行这句:rm -rf /etc/sysconfig/iptables 直接进入修改:vi /etc/sysconfig/iptables 添加8 ...

  9. 一个事件激活多个JavaScript函数

    http://www.cnblogs.com/meil/archive/2006/09/20/509359.html如果你的网页中一个“OnLoad”事件要激活两个以上的JavaScript函数,那怎 ...

  10. PHP权限控制(转)

    PHP: 我这里说到的权限管理办法是一个普遍采用的方法,主要是使用到"位运行符"操作,& 位与运算符.| 位或运行符.参与运算的如果是10进制数,则会被转换至2进制数参与运 ...