转载请注明出处:点我

这是一系列的文章,会从基础开始一步步的介绍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. zend studio 13.6.1 安装+破解+汉化

    zend studio 13.6.1 X64 安装+破解+汉化+补丁 一.下载相关文件 1.官网原版下载 : http://downloads.zend.com/studio-eclipse/13.6 ...

  2. (C++)浅谈using namespace std

    1.<iostream>和<iostream.h> 在你的编译器include文件夹里面可以看到,二者是两个文件,里面的代码是不一样的. 后缀为.h的头文件c++标准已经明确提 ...

  3. C#基础视频教程3.3 常见控件类型和使用方法

    前面介绍了微软的控件,也介绍了几个第三方控件,那么这些控件是如何做出来的?即便我们自己不做控件,也至少要理解控件的原理. 如果要创建一个自定义控件,首先新建一个C#下面的Windows窗体控件库   ...

  4. POI生成EXCEL文件(字体、样式、单元格合并、计算公式)

    创建一个封装类: package com.jason.excel; import java.io.FileNotFoundException; import java.io.FileOutputStr ...

  5. 将图片转成base64字符串并在JSP页面显示的Java代码

    *本事例主要讲了如下几点:  * 1:将图片转换为BASE64加密字符串.  * 2:将图片流转换为BASE64加密字符串.  * 3:将BASE64加密字符串转换为图片. * 4:在jsp文件中以引 ...

  6. view xml 中的 button 调用web客户端事件

    最近写一个模块 需要 在客户端干点事. 按常规的方法, 应该是写个 客户端模块. 在 客户端 init, start, render 去渲染个按钮出来干事.暂时还不太理解WEB模块如何很好地同服务器端 ...

  7. Linux下TCP/IP内核参数优化

    /proc/sys/net目录 所有的TCP/IP参数都位于/proc/sys/net目录下(请注意,对/proc/sys/net目录下内容的修改都是临时的,任何修改在系统重启后都会丢失),例如下面这 ...

  8. POJ 2750 Potted Flower (单点改动求线段树上最大子序列和)

    题目大意: 在一个序列上每次改动一个值,然后求出它的最大的子序列和. 思路分析: 首先我们不考虑不成环的问题.那就是直接求每一个区间的最大值就好了. 可是此处成环,那么看一下以下例子. 5 1 -2 ...

  9. centos 搭建 搭建uwsgi服务

    1. 安装linux系统所需的一些软件依赖 yum groupinstall "Development tools" yum install openssl openssl-dev ...

  10. Python根据系统环境配置日志,Python配置日志 Python logger

    我们通常在写爬虫的时候,需要配置日志,但是有可能是在windows开发的,但是程序的运行环境可是是在Linux中,这时候我们就需要不停的更换日志的目录了 但是我们可以实现通过判断不同的运行环境,来时间 ...