转载请注明出处:点我

这是一系列的文章,会从基础开始一步步的介绍Python中的Generator以及coroutine(协程)(主要是介绍coroutine),并且详细的讲述了Python中coroutine的各种高级用法,最后会用coroutine实现一个简单的多任务的操作系统。

其实也是看完这篇文章的学习笔记吧!O(∩_∩)O

生成器(Generator)

什么是生成器?在Python中,生成器(Generator)是一个带有yield关键字的函数

 def gene():
a = 1
print "开始执行gene(),a = ",a
a += 1
yield "rio_2607"
print "回到gene(),再辞开始执行,a = ",a
a += 2
yield "uestc"
print "又回到了gene(),a = ",a
yield "emc"

gene()就是一个生成器,因为函数的定义中有yield关键字。那么生成器跟普通的函数有什么区别呢?

当调用gene()函数的时候,不会立即执行gene()函数中的代码,而是返回一个生成器对象(generator object):

 >>> def gene():
a = 1
print "开始执行gene(),a = ",a
a += 1
yield "rio_2607"
print "回到gene(),再辞开始执行,a = ",a
a += 2
yield "uestc"
print "又回到了gene(),a = ",a
yield "emc" >>> g = gene()
>>> type(g)
<type 'generator'>

可以看到,g是一个generator类型的对象。那么什么时候会执行函数的代码呢?答:当调用生成器对象的next()函数时就会开始执行函数定义中的代码。

但是跟普通函数一旦开始执行就会一直执行直到结束不同,生成器函数会一直往下执行,但是一旦碰到yield关键字,就会返回yield关键字后面的数据,把函数当前的所有状态封存起来,然后暂停函数的执行,在生成器对象的next()函数再一次被调用的时候,会接着上一次暂停的地方继续往下执行,直到碰到了下一个yield关键字或者函数的代码执行完毕

>>> g = gene()
>>> type(g)
<type 'generator'>
>>> g.next()
开始执行gene(),a = 1
'rio_2607'
>>> g.next()
回到gene(),再辞开始执行,a = 2
'uestc'
>>> g.next()
又回到了gene(),a = 4
'emc'

可以看到,第一次调用g.next()函数时,函数内部的代码才开始执行,当执行到yield "rio_2607"这一句代码时,会返回"rio_2607",然后函数暂停执行。然后当再次调用next函数的时候,gene()函数会接着往下面执行,可以看到,这时打印出来的a=2,保持了函数上一次离开时候的数据,当碰到yield "uestc"这一句时,函数会再次停止执行,封存此时函数内的数据。当再一次调用next()函数的时候,gene()会接着上次的状态,在上次暂停的地方继续往下执行,可以看到,此时打印输出了a=4,碰到yield之后再次暂停执行。

当生成器执行完毕后,再一次调用next()时,函数会抛出StopIteration异常

>>> g.next()
又回到了gene(),a = 4
'emc'
>>> g.next() Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
g.next()
StopIteration

生成器表达式(Generator Expresisions)

生成器表达式(Generator Expresisions)类似于列表推导式(list comprehension)

ge = (x * 2 for x in a)

其中(x * 2 for x in a)就是生成器表达式,这个表达式会返回一个生成器对象:

>>> ge = (x * 2 for x in a)
>>> ge
<generator object <genexpr> at 0x01EA0A30>

在for循环中,for循环会自动调用生成器对象的next()函数并处理StopIteration异常:

>>> ge
<generator object <genexpr> at 0x01EA0A30>
>>> for i in ge:
print i 2
4
6
8

说了那么多,那么生成器除了实现迭代器(Iteration)之外,还有有什么作用呢?

我们有这么一个web server上面的log文件,数据大概是这样的

77.81.4.30 - - [24/Feb/2008:02:17:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133
24.1.247.118 - - [24/Feb/2008:02:20:25 -0600] "GET /dynamic/ HTTP/1.1" 200 5105
24.1.247.118 - - [24/Feb/2008:02:20:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133
24.1.247.118 - - [24/Feb/2008:02:20:26 -0600] "GET /favicon.ico HTTP/1.1" 404 133
122.117.168.219 - - [24/Feb/2008:02:22:06 -0600] "GET /ply/ HTTP/1.1" 304 -
122.117.168.219 - - [24/Feb/2008:02:22:06 -0600] "GET /ply/bookplug.gif HTTP/1.1" 304 -
122.117.168.219 - - [24/Feb/2008:02:22:08 -0600] "GET /ply/example.html HTTP/1.1" 304 -
89.182.136.236 - - [24/Feb/2008:02:23:04 -0600] "GET /ply/ HTTP/1.1" 200 8018
89.182.136.236 - - [24/Feb/2008:02:23:05 -0600] "GET /ply/bookplug.gif HTTP/1.1" 200 23903
89.182.136.236 - - [24/Feb/2008:02:23:05 -0600] "GET /favicon.ico HTTP/1.1" 404 133
66.249.65.37 - - [24/Feb/2008:02:23:29 -0600] "GET /papers/SIAM97/SIAM97.pdf HTTP/1.1" 200 188949
117.198.144.124 - - [24/Feb/2008:02:23:50 -0600] "GET /ply/ply.html HTTP/1.1" 200 97238
117.198.144.124 - - [24/Feb/2008:02:23:53 -0600] "GET /favicon.ico HTTP/1.1" 404 133

每一行的最后一列要么表示一个字节数据,要么为-,表示字节数据未知

现在我们要统计文件中记录的所有的字节数据大小

python中常规的写法是这样的,在一个for循环中,每次处理一行数据:

 def non_generator_func():
'''
分析web server的log文件来判断所有传输的字节数
Non-Generator的写法:用一个for循环
:return:
'''
wwwlog = open("access-log")
total = 0 for line in wwwlog:
# 获取字节数的字符串表示
bytestr = line.rsplit(None, 1)[1]
if bytestr != "-":
total += int(bytestr) print "Total", total

现在来看看使用Generator的风格编写的代码:

 def generator_func():
wwwlog = open("access-log")
# 采用生成器表达式(Generator expression),返回一个Generator对象
bytecolumn = (line.rsplit(None, 1)[1] for line in wwwlog)
bytes = (int(x) for x in bytecolumn if x != "-") # 最后一步才进行计算
print "Total", sum(bytes)

可以看出,使用Generator,可以编写更少的代码,还会有跟普通的Python编程完全不一样的编程风格。

上面的generator_func()函数的工作方式类似于管道(pipeline):

 access-log  ---> wwwlog ---> bytecollumn --->bytes --->sum() --->total

现在来看另外一个Generator Fucntion的典型用法:在这里,我们模拟实现Unix中的"tail -f"命令。照例先看代码:

 # tail -f:可以实时的得到新追加到文件中的信息,常用来跟踪日志文件
def unix_tail_f(thefile):
'''
Python版本的 Unix 'tail -f'
'''
import time
# 跳到文件末尾
thefile.seek(0,2)
while True:
line = thefile.readline()
if not line:
time.sleep(0.1)
continue
yield line

通过下面的方式来使用unix_tail_f()函数:

 logfile = open("access-log")
for line in follow(logfile):
  print line,

可以看出,通常使用Generator Fucntion的模式应该为:

现在,我们已经实现了tail -f的效果,接下来我们要更进一步,实现tail -f | grep 的过滤效果。先编写一个Generator Function,名字叫做grep,代码如下:

 def grep(pattern,lines):
for line in lines:
if pattern in line:
# 如果line中有pattern,则返回这个line并挂起,暂停执行
yield line

下面的代码能够达到unix中的tail -f | grep pattern的效果:

 def tail_f_grep(file,pattern):
'''
模拟tail -f | grep pattern
'''
logfile = open(file)
loglines = unix_tail_f(logfile)
pylines = grep(pattern,loglines) # 在for循环中处理结果
for line in pylines:
print line,

当调用tail_f_grep("access-log","python")可以达到tail -f | grep python的效果。

关于Python中的生成器,Python函数式编程指南(四):生成器这篇博客讲的挺好的,大家可以看下这篇博客。

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

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

    原创作品,转载请注明出处:点我 上一篇文章Python高级编程之生成器(Generator)与coroutine(一):Generator中,我们介绍了什么是Generator,以及写了几个使用Gen ...

  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. Elasticsearch 数据搜索篇

    curl 'localhost:9200/_cat/indices?v' health index pri rep docs.count docs.deleted store.size pri.sto ...

  2. 出现RST的几种情况

    1.端口未打开,C向S发送SYN,去连接S的端口9820,但是S没有打开9820端口,这个时候S发送RST 2.请求超时,C向S发送SYN,S回复ACK+SYN,如果C从发送SYN到收到S的ACK+S ...

  3. WCF 之 生成元数据和代理

    在WCF开发概述中讲解了手工方式的WCF应用,其实实际开发中使用更多的使用配置方式和元数据来实现WCF,下面我们来看一个具体的Demo,这个例子和WCF开发概述中使用的是同一个例子,只是实现方式不同, ...

  4. java实现双向循环链表

    java实现循环链表:http://www.cnblogs.com/lixiaolun/p/4643911.html 在单链表中,查询下一个元素的时间是O(1).查询上一个元素的时间却是O(n). 为 ...

  5. 扩展MSEG 加入Z字段

    append    MSEG append    IMSEG append    BAPI_TE_XMSEG 实现 BADI: MB_BAPI_GOODSMVT_CREATE BAPI中: BAPI_ ...

  6. QtGui.QBrush

    The QtGui.QBrush is an elementary graphics object. It is used to paint the background of graphics sh ...

  7. 《学习opencv》笔记——矩阵和图像操作——cvCrossProduct and cvCvtColor

    矩阵和图像的操作 (1)cvCrossProduct函数 其结构 void cvCrossProdust(//计算两个三维向量的叉积 const CvArr* src1, const CvArr* s ...

  8. shell学习三十二天----read读取一行

    标准输入输出与标准错误输出 标准输入/输出可能是软件工具设计原则里最主要的观念了.他的构想是:程序应有一个数据来源,数据出口(数据要去哪里),以及报告问题的地方.他们分别叫做标准输入,标准输出和标准错 ...

  9. Spring 切面优先级

    之前我们提过的应用场景,一个原始对象可能会需要插入多个切面,如果我们按前几篇博客文章介绍的方法完成切面及其通知的注解声明,那么它的执行顺序是怎么样的呢? 本文将介绍AspectJ的切面如何划分优先级 ...

  10. 基于Scrapy框架的Python新闻爬虫

    概述 该项目是基于Scrapy框架的Python新闻爬虫,能够爬取网易,搜狐,凤凰和澎湃网站上的新闻,将标题,内容,评论,时间等内容整理并保存到本地 详细 代码下载:http://www.demoda ...