迭代器和生成器是python中的重要特性,本章作者花了很大的篇幅来介绍迭代器和生成器的用法.
首先来看一个单词序列的例子:
import re
re_word=re.compile(r'\w+')
class Sentence(object):
    def __init__(self,text):
        self.text=text
        self.word=re_word.findall(text)
    def __getitem__(self, item):
        return self.word[item]
    def __len__(self):
        return len(self.word)
    def __str__(self):
        return 'Sentence(%s)' % self.word if __name__=="__main__":
    s=Sentence("Today is Tuesday")
    print s
    for word in s:
        print word
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter14.py
Sentence(['Today', 'is', 'Tuesday'])
Today
is
Tuesday
在上面的这个例子中,for word in s 是在迭达整个序列。但是我们知道一个对象可以迭代是因为实现了__iter__方法,但是在Sentence中并没有实现__iter__方法。那为什么可以迭代呢
原因在于在python中实现了iter和getitem的都是可迭代的。首先会检查是否实现了iter方法,如果实现了则调用,如果没有但是实现了__item__方法。Python就会创建一个迭代器。尝试按照顺序获取元素。如果尝试失败则会抛出typeerror异常,提示object is not iterable.
因此如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的。如果实现了__getitem__方法,而且其参数是从零开始的索引。这种对象也可以迭代。我们用__iter__方法来改造之前的Sentence。在__iter__中返回一个可迭代对象iter(self.word)。当执行for word in s的时候就会调用__iter__方法
import re
re_word=re.compile(r'\w+')
class Sentence(object):
    def __init__(self,text):
        self.text=text
        self.word=re_word.findall(text)
    def __iter__(self):
        return iter(self.word)
    def __len__(self):
        return len(self.word)
    def __str__(self):
        return 'Sentence(%s)' % self.word if __name__=="__main__":
    s=Sentence("Today is Tuesday")
    print s
    for word in s:
        print word
我们在来看下next, next的作用是返回下一个元素,如果没有元素了,抛出stopIteration异常。我们来看下next的使用方法。如果要遍历一个字符串,最简单的方法如下:
s='abc'
for
char in s:
    print char
如果不用for方法,代码需要修改如下:
s='abc'
it=iter(s)
while True:
    try:
        print next(it)
    except StopIteration:
        del it
        break
首先将s变成一个iter对象,然后不断调用next获取下一个字符,如果没有字符了,则会抛出StopIteration异常释放对it的引用。废弃迭代对象。
比如下面的用法则会抛出异常:
s='abc'
it=iter(s)
print it.next()
print it.next()
print it.next()
print it.next()
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter14.py
Traceback (most recent call last):
  File "E:/py_prj/fluent_python/chapter14.py", line 21, in <module>
    print it.next()
StopIteration
a
b
c
因为只有3个字符,但是调用了4次it.next()导致已经找不到字符因此抛出异常。
将到这里,我们需要解释两个概念,可迭代对象和迭代器
可迭代对象:实现了__iter__方法,就是可迭代的,可以返回自身作为迭代器。也可以返回其他一个可迭代对象
迭代器:在Python2中实现了next方法,在python3中实现了__next__方法。
我们来看下下面的这个图,首先要让x通过iter变成一个可迭代对象,然后使用迭代器来调用元素

我们将上面Sentence的代码修改下:
class Sentence(object):
    def __init__(self,text):
        self.text=text
        self.word=re_word.findall(text)
        self.index=0
    def __iter__(self):
        return self             ⑴
    def next(self):             ⑵
        try:
            word=self.word[self.index]
        except IndexError:
            raise StopIteration
        self.index+=1
        return word
    def __len__(self):
        return len(self.word)
    def __str__(self):
        return 'Sentence(%s)' % self.word if __name__=="__main__":
    s=Sentence('Today is tuesday')
    for word in s:
        print word
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter14.py
Today
is
Tuesday
在(1)中,首先这个类是可迭代的,因为具有__iter__方法,也是它自身的迭代器,因为具有next方法
在(2)中,由于实例是可迭代对象,因此可以不停的获取下一个元素
这和之前的实现有什么区别呢:
之前的代码虽然也实现了__iter__,但是返回的却是另外一个可迭代对象:iter(self.word),因此当我们使用for word in s的时候,其实是在迭代iter(self.word)
def __iter__(self):
    return iter(self.word)
而如下的实现,其实是在迭代Sentence本身
def __iter__(self):
    return self           
def next(self):
    try:
        word=self.word[self.index]
    except IndexError:
        raise StopIteration
    self.index+=1
    return word
这里总结一下:
1 可迭代对象是迭代器的一个身份证明,只有是可迭代对象才能实现迭代器
2 迭代器其实具体工作的方法,就好比可迭代对象是政府,迭代器是公务员,只有依托于政府,公务员的工作才能进行。
 
 
下面介绍另外一个高级应用:生成器 yield.在函数中实现了yield的都是生成器函数。我们来看一个简单的例子:
def odd():
    n=1
    while True:
        yield n
        n+=2 if __name__=="__main__":
    for o in odd():
        print o
        if o > 12:
            break
在这里odd函数就是一个生成器。每当for o in odd()的时候。会自动生成一个值o。生成器的工作原理如下:
在每次for循环执行的时候,每次循环都会执行odd函数内部的代码,执行到yield n的时候就返回一个迭代值,并且保存当时所有的函数变量。下次迭代的时候,代码从yield  n的下一条语句继续执行。看到这你会先到中断,对的yield就是有采用中断的方法。而且生成器从本质上说也是一个可迭代对象。也就是说实现了yield的函数既是一个可迭代对象,也是一个迭代器。我们来看下前面odd的另外一种用法:
o=odd()
print next(o)
print next(o)
print next(o)
print next(o)
print next(o)
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter14.py
1
3
5
7
9
用next方法可以不停的生成返回的值。直到执行了o.close()关闭生成器。
在这里我们归纳下生成器,迭代器,可迭代对象之间的关系,用下面的图来表示,从图上可以看到,生成器是可迭代对象和迭代器的一种高级封装。

有了生成器,我们就可以将之前的Sentence改造如下:
class Sentence(object):
    def __init__(self,text):
        self.text=text
        self.word=re_word.findall(text)
        self.index=0
    def __iter__(self):
        for word in self.word:
            yield word
    def __len__(self):
        return len(self.word)
    def __str__(self):
        return 'Sentence(%s)' % self.word
 
那么生成器除了能实现可迭代对象和迭代器,还有其他好处么。我们首先来看下这样的一种应用。我们想实现一个函数,这个函数返回值是得到100之内的所有数的平方值,我们根据这个返回值然后对各个值进行处理。一般来说我们会这样实现:
def data_generate(value):
    number=[]
    for i in range(value):
        num=i*i
        number.append(num)
    return number
首先定义一个列表,然后将value内的值全部取平方。然后加入到number中去。等所有的数都生成后直接用return返回。这里看上去没啥问题。但是如果我们设置的value是10000或者是更大的数。那么对应的列表number也会变得更大。这样就需要更多的内存来存储值。如果这个value足够大,仅仅为了存储这些值就得耗尽所有的内存,哪该怎么办呢。有没有一种方法每当生成一个数的时候,就返回这个值,这样就不需要专门定义一个列表来存储了。
但是return语句每当调用的时候整个函数就停止了。无法满足我们的诉求。不用急,python中的生成器完全我们的需求。而且用法很简单
代码改造成如下
def data_generate(value):
    for i in range(value):
        num=i*i
        yield num
如下调用
for i in data_generate(100):
    print i
 
通过代码可以看到我们去掉了number列表以及return语句。添加了yield num语句。并用调用迭代器的方式调用data_generate函数。最终也达到了我们要的效果。而且最重要的是在函数中我们不需要申请一个占用内存的列表。完美的实现了我们的诉求。
 
那么我们可以用生成器来优化之前的Sentence实例。在之前的__init__实现中,我们首先用self.word=re_word.findall(text)将所有匹配的字符提取出来存入到self.word中,如果文本内容很大,那么就需要开辟一个大内存的列表来存储。如果我们只需要迭代前面几个单词,那么多余的内存就是不必要的。因此我们用生成器来优化下。re.finditer是函数re.findall的变种,返回的不是列表,而是一个生成器。代码改成如下:这里省去了self.word的赋值。也就不用开辟一块内存来专门存储。这样极大的节约了内存代码也变得更简短
class Sentence(object):
    def __init__(self,text):
        self.text=text
    def __iter__(self):
        for match in re.finditer(self.text):
            yield match.group()
    def __len__(self):
        return len(self.word)
    def __str__(self):
        return 'Sentence(%s)' % self.word
另外生成器还可以用在列表推导上:
l=[x*x for x in range(10)]
l1=(x*x for x in range(10))
l是一个列表,而l1是一个生成器。也能达到节省内存的作用。
 
在python的自带库里面,也有很多生成器。
Filter(predict,it)把it中的各个元素传给predict,如果predict返回true,那么产出对应的元素。
def vowel(c):
    return c.lower() in 'aeiou' if __name__=="__main__":
    print list(filter(vowel,'Aardvark'))
ifilterfalse:和filter相反,当predict返回False的时候才产出对应的元素
print list(itertools.ifilterfalse(vowel,'Aardvark'))
 
itertools.chain(it1,….itn):先产出it1中的元素,然后产出it2中的元素,以此类推,无缝连接在一起
print list(itertools.chain('abc',range(2)))
类似的在itertools中还有很多,这里就不一一介绍了
 
最后来看下iter函数的一个用法。Iter函数可以传入2个参数。第一个参数可以是可调用的对象,用于不断调用产出各个值,第二值是标记符,当可调用对象返回这个值的时候,触发迭代器抛出StopIteration异常。示例代码如下:
def d6():
    return randint(1,6) if __name__=="__main__":
    d6_iter=iter(d6,1)
    for roll in d6_iter:
        print roll
iter(d6,1),首先采用d6函数不断产生整数,直到产生的数为1的时候停止。

												

流畅python学习笔记:第十四章:迭代器和生成器的更多相关文章

  1. Python学习笔记(十四)

    Python学习笔记(十四): Json and Pickle模块 shelve模块 1. Json and Pickle模块 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不 ...

  2. 《机器学习实战》学习笔记第十四章 —— 利用SVD简化数据

    相关博客: 吴恩达机器学习笔记(八) —— 降维与主成分分析法(PCA) <机器学习实战>学习笔记第十三章 —— 利用PCA来简化数据 奇异值分解(SVD)原理与在降维中的应用 机器学习( ...

  3. Python学习笔记(十四):模块高级

    以Mark Lutz著的<Python学习手册>为教程,每天花1个小时左右时间学习,争取两周完成. --- 写在前面的话 2013-7-23 21:30 学习笔记 1,包导入是把计算机上的 ...

  4. python3-cookbook笔记:第四章 迭代器与生成器

    python3-cookbook中每个小节以问题.解决方案和讨论三个部分探讨了Python3在某类问题中的最优解决方式,或者说是探讨Python3本身的数据结构.函数.类等特性在某类问题上如何更好地使 ...

  5. 流畅python学习笔记:第十一章:抽象基类

    __getitem__实现可迭代对象.要将一个对象变成一个可迭代的对象,通常都要实现__iter__.但是如果没有__iter__的话,实现了__getitem__也可以实现迭代.我们还是用第一章扑克 ...

  6. 流畅python学习笔记:第十七章:并发处理

    第十七章:并发处理 本章主要讨论Python3引入的concurrent.futures模块.在python2.7中需要用pip install futures来安装.concurrent.futur ...

  7. 流畅python学习笔记:第十七章:并发处理二

    本章讨论python3.2引入的concurrent.futures模块.future是中文名叫期物.期物是一种对象,表示异步执行的操作 在很多任务中,特别是处理网络I/O.需要使用并发,因为网络有很 ...

  8. 学习笔记 第十四章 使用CSS3动画

    第14章   使用CSS3动画 [学习重点] 设计2D动画 设计3D动画 设计过渡动画 设计帧动画 能够使用CSS3动画功能设计页面特效样式 14.1  设计2D动画 CSS2D Transform表 ...

  9. python学习笔记-(十四)I/O多路复用 阻塞、非阻塞、同步、异步

    1. 概念说明 1.1 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可 ...

  10. python学习笔记-(十四)进程&协程

    一. 进程 1. 多进程multiprocessing multiprocessing包是Python中的多进程管理包,是一个跨平台版本的多进程模块.与threading.Thread类似,它可以利用 ...

随机推荐

  1. 盘点支持Orchard的.NET 4.5虚拟主机(虚拟空间)

    Orchard作为.NET社区最为先进的开源项目之一,已经被越来越多的人使用, 由于它使用了最时髦的微软技术栈(.NET4.5),因此在 Win2008+和IIS7+ 的环境上运行最为完美, 而win ...

  2. 基于Tiny4412的I2C驱动分析

    本文以tiny4412平台上到三轴加速度器为例简单分析了Linux下到i2c驱动编程. http://pan.baidu.com/s/1c0H5vRq

  3. 【APT】NodeJS 应用仓库钓鱼,大规模入侵开发人员电脑,批量渗透各大公司内网

    APT][社工]NodeJS 应用仓库钓鱼,大规模入侵开发人员电脑,批量渗透各大公司内网 前言 城堡总是从内部攻破的.再强大的系统,也得通过人来控制.如果将入侵直接从人这个环节发起,那么再坚固的防线, ...

  4. js中推断浏览器类型

    在实际看发展.有时候会遇到在IOS和Android中要用不同的方法处理网页.须要让网页返回当前浏览器的类型. /** * 推断浏览器类型 */ var Browse = function () { / ...

  5. UNP学习笔记(第二章:传输层)

    本章的焦点是传输层,包括TCP.UDP和SCTP. 绝大多数客户/服务器网络应用使用TCP或UDP.SCTP是一个较新的协议. UDP是一个简单的.不可靠的数据报协议.而TCP是一个复杂.可靠的字节流 ...

  6. ps快捷键记录

    alt+delete  前景色填充 ctrl+delete  背景色填充 alt+shift+鼠标调节  变换选取,做圆环 ctrl+t  自由变换 alt+鼠标拖动  快捷复制某区域 delete ...

  7. Amazon DynamoDB, 面向互联网应用的高性能、可扩展的NoSQL数据库

    DynamoDB是一款全面托管的NoSQL数据库服务.客户能够很easy地使用DynamoDB的服务.同一时候享受到高性能,海量扩展性和数据的持久性保护. DynamoDB数据库是Amazon在201 ...

  8. url删除指定字符

    var str = "http://www.xxx.com/?pn=0"; // 删除指定字符 pn=0 // 我将这个字符串里所可能想到的各种情况都列举出来 var a = [ ...

  9. android历史

    Android一词最早是出如今法国作家维里耶德利尔·亚当1986年发表的<未来夏娃>这部科幻小说中,作者利尔·亚当将外表像人类的机器起名为Android.这就是Android小人名字的由来 ...

  10. c++实现二叉搜索树

    自己实现了一下二叉搜索树的数据结构.记录一下: #include <iostream> using namespace std; struct TreeNode{ int val; Tre ...