最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • PYTHON 生成器和迭代器

    正文概述 掘金(reposkeeper)   2020-11-26   429

    PYTHON 生成器和迭代器

    很多Python的程序员都会混淆 迭代器生成器 的概念和作用,分不清到底两个有什么区别。今天我们来好好说一说这两个概念。

    迭代器(Iterator)

    Iterator Pattern

    Iterator 是一种设计模式,它的作用是,提供一种顺序访问一个聚合对象中的各个元素,但又不需要暴露出其内部实现的方法。它是一种惰性的获取数据的方法,我们不需要一次把所有的数据载入内存,这样可以避免数据集太大,内存无法全部装载的麻烦。 这种应用场景,比如:读取一个大文件,分析每一行的关键字

    一个最简单的迭代器模式,表现为一个接口,接口中包含两个方法:

    1. Next() 返回下一个元素
    2. hasNext() 返回是否还有下一个元素

    实现了这种两个方法的对象就是一个迭代器。

    Python中的Iterator

    在很多时候,Python程序员会忽略 迭代器(Iterator) 和 可迭代对象(Iterable Object) 的区别。

    其实,我们要好好的区分一下他们两个。

    可迭代对象(Iterable Object)

    可迭代对象是表示一个对象,拥有一次返回一个他自己的数据元素的能力。

    例如:

    In [1]: a = [1, 2, 3, 4, 5]
    
    In [2]: for i in a:
       ...:     print(i)
       ...:
    1
    2
    3
    4
    5
    
    In [3]: b = {"first":1, "second":2, "third":3}
    
    In [4]: for i in b:
       ...:     print i
       ...:
    second
    third
    first
    

    上面的代码通过迭代的方式输出了list中所有的元素,和dict中所有的key。所以,我们把list和dict叫做可迭代对象(不是迭代器)。

    在Python中,所有的集合都可以迭代。在语言内部,迭代器支持下面列出的操作:

    • for循环
    • 遍历文件、目录
    • 列表推导、字典推导和集合推导
    • 元组拆包
    • 调用函数时,使用 * 拆包实参
    • 构建和扩展集合类型

    所以可以看到,迭代操作在python中很多地方都很重要。

    序列可以迭代的原因

    这依赖一个buildin-function iter()。假如解释器要迭代对象x,则会调用 iter() 产生一个迭代器,进行迭代。

    内置的 iter 函数有以下作用:

    1. 检查对象是否实现了 __iter__ 方法,如果实现了就调用它,获取一个迭代器。
    2. 如果没有实现 __iter__ 方法, 但是实现了 __getitem__ 方法, Python 会创建一个迭代 器,尝试按顺序(从索引 0 开始)获取元素。
    3. 如果尝试失败, Python 抛出 TypeError 异常, 通常会提示“X object is not iterable”。
    In [8]: x = 2
    In [9]: iter(x)
    -----------------------------------------------------
    TypeError           Traceback (most recent call last)
    <ipython-input-9-128770259dbe> in <module>()
    ----> 1 iter(x)
    
    TypeError: 'int' object is not iterable
    

    标准序列都实现了 _getitem_ 方法。 其实,它们也都实现了 _iter_ 方法,因此你也应该这么做。之所以都实现 _getitem_ 是因为要向后兼容,但后续可能废弃。

    如何实现可迭代对象

    自己创建的 Object 如何变成一个可迭代的对象呢?如何自己创建一个迭代器呢?其实非常简单。

    对于可迭代对象,需要满足下面两个要求的任意一个(原因参见上面):

    1. 拥有 __getitem__ 方法;接受一个参数 index
    2. 拥有 __iter__ 方法;返回一个 Iterator

    例:

    #!/usr/bin/env python
    class MyIterableObject():
    
        def __init__(self, s):
            self.seq = s.split(' ')
    
        def __getitem__(self, index):
            return self.seq[index]
            
        def __iter__(self):  
            return MyIterator(self.seq) # MyIterator的具体实现参见后面
    
    
    if __name__ == '__main__':
    
        mio = MyIterableObject("a b c d e f g")
    
        for i in mio:
            print(i)
    

    迭代器(Iterator)

    当用 iter 函数获取到一个迭代器之后,就可以操作迭代器来获取对象的数据了。

    使用 next() 方法来一个个的获取元素。当所有元素获取完毕,继续调用 next() 方法的话,就会抛出一个 StopIteration 的异常。

    如下:

    In [13]: a = [1, 2, 3, 4, 5]
    In [14]: i = iter(a)
    In [15]: while True:
        ...:     print(next(i))
        ...:
    1
    2
    3
    4
    5
    -----------------------------------------------------
    StopIteration       Traceback (most recent call last)
    <ipython-input-15-ac43f8f9aeeb> in <module>()
          1 while True:
    ----> 2     print(next(i))
          3
    
    StopIteration:
    

    python的迭代器较为简单,它并不支持重新定位到开始这样的操作。如果一个迭代器一旦开始使用,如果想要从最开始读取的话,只能创建一个新的迭代器了。

    如何实现迭代器

    标准的python迭代器需要实现两个方法:

    1. __iter__ 返回迭代器本身
    2. next() 返回数据集中的下一个元素。如果没有下一个了,则抛出一个 StopIteration
    class MyIterator():
    
        def __init__(self, s):
            
            self.seq = s
            self.len = len(self.seq)
            self.index = 0
    
        def __iter__(self):
            return self
    
        def next(self):
            try:
                n = self.seq[self.index]
            except IndexError:
                raise StopIteration
    
            self.index += 1
    
            return n
    

    这里有一点需要注意:迭代器模式描述中,需要有一个方法来判断是否是最后一个元素,在python中使用异常代替了这个函数。在我们使用迭代器的过程中,捕获这个异常即可。如果使用 buildin 的 for .. in 方式的话,它会自动帮我们捕获。

    生成器(Generator)

    首先,我们平常说起来 Generator 这个东西的时候,其实,它一般指代两个东西:

    1. Generator Function: 一个函数,在定义时使用了 yield 关键字,则成为这个函数为 生成器函数
    2. Generator Object: 由 Generator Function 生成的,是一个特殊的 Iterator。它包装了 生成器函数 的定义体,并实现了 __iter__next 两个方法,符合 Iterator 的协议。

    生成器和迭代器最大的不同在哪里呢?

    主要是对于值产生的方法不一样。当使用迭代器时,所有要迭代的元素必须是已经存在的。而对于生成器来说,每个值不必已经存在,可以在执行的过程中计算(生成)出来。

    比如:用生成器 生成一个等比数列

    def arithmetic_progression(base, dif, count):
        for n in range(count):
            yield base + dif * n
    
    
    if __name__ == '__main__':
    
        for i in arithmetic_progression(1, 3, 10):
            print(i)
    

    可以看到这个等比数列是不存在的,是在迭代的过程中每次执行到 yield 的时候,计算出来的。

    可以达到这样的特性归功于 yield 关键字。它可以将执行的函数暂停,并返回值,下一次从中断的地方继续。它的执行流程如下:

    1. 使用 next 调用 生成器函数
    2. 函数 执行到 yield,会返回一个值,并暂停函数
    3. 重复 1-2 步,直到所有的值都返回完毕
    4. 如果使用 next,则会抛出 StopIteration

    代码验证如下:

    In [21]: def test():
        ...:     yield 1
        ...:     yield 2
        ...:     yield 3
        ...:
    
    In [22]: gen = test()
    
    In [23]: next(gen)
    Out[23]: 1
    
    In [24]: next(gen)
    Out[24]: 2
    
    In [25]: next(gen)
    Out[25]: 3
    
    In [26]: next(gen)
    -----------------------------------------------------
    StopIteration       Traceback (most recent call last)
    <ipython-input-26-8a6233884a6c> in <module>()
    ----> 1 next(gen)
    
    StopIteration:
    

    用生成器代替迭代器

    现在我们用生成器来替代上面的 迭代器方案 MyIterableObject

    class MyGenerator():
    
        def __init__(self, s):
            self.seq = s.split(' ')
    
        def __iter__(self):
            for s in self.seq:
                yield s
    

    代码简化了很多,我们不需要再自己创建 Iterator 对象,yield会帮我们做这些。

    迭代器工具集(itertools)

    虽然,生成器的使用已经够简单了,但是像python这种节省你生命时间的语言,怎么会没有更进一步的包装出来?

    python内置了非常多的生成器函数,比如遍历文件夹的 os.walk,工具类的有 mapenumerate 等等。还有一个官方库,叫做 itertools,它包含了 19 个生成器函数,可以组合完成各样的功能。

    结尾

    以上,就是 迭代器和生成器的区别。其实,这两个东西并不难理解。但是,这里面有几个比较容易混淆的概念。只要搞清楚了这些概念,就能区分得很清楚。


    起源地下载网 » PYTHON 生成器和迭代器

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元