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


    迭代器和生成器是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.11python.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.11python.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.11python.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.11python.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的时候停止。
    
    
    
    
    
  • 相关阅读:
    Git 学习笔记(W,I,P)
    DirectX API 编程起步 #01 项目设置
    #1004 Let the Balloon Rise
    #1003 Max Sum
    人生的第一个博客(●'◡'●)ノ♥--开博典礼
    2053——switch game
    在Activity间传递数据的四种方法及返回结果
    安卓学习第38课——ProgressDialog
    安卓学习第37课——DatePickerDialog、TimePickerDialog
    安卓学习第36课——PopupWindow
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/7248799.html
Copyright © 2020-2023  润新知