流畅python学习笔记:第十四章:迭代器和生成器

mac2022-06-30  77

迭代器和生成器是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.wordif __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.wordif __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.wordif __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+=2if __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 9next方法可以不停的生成返回的值。直到执行了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的时候停止。

 

 

转载于:https://www.cnblogs.com/zhanghongfeng/p/7248799.html

相关资源:Python学习笔记之迭代器和生成器用法实例详解
最新回复(0)