python 中的迭代器和生成器详解

迭代器

     学习过python的童鞋都应该知道python中有三大神器,即 迭代器、生成器和装饰器,所谓神器,必有其神奇之处,今天和大家一起学习一下迭代器和生成器。
     迭代是python比较强大的功能之一,说起迭代,我们可能会想到,对于一个列表list,一个元组tuple,访问的时候都可以使用 for .. in .. ,没错,这就是迭代,它是访问集合元素的一种方式,在从头到尾遍历可迭代对象时,能够记住遍历的位置。
     这里给大家区分两个概念 可迭代对象(iterable)和迭代器(iterator), 我们往往说某个对象是不是可迭代的,而迭代器可以抽象为一个‘器’,就是在从头到尾遍历可迭代对象中的某一元素时,能够记住遍历的位置的那玩意儿,二者之间有本质区别,下面举例说明一下

    from collections import Iterable

    test = ['a', 'b']
    for i in test:
        print(i)
    # 输出结果,说明列表是可迭代的
    a
    b

    isinstance([], Iterable)
    True
    
    for i in 10:
        print(i)
    #输出结果,说明整型数字是不可迭代的
    TypeError: 'int' object is not iterable

    isinstance(10, Iterable)
    False

     上面我们看到的是可迭代对象的外在表现,那么可迭代对象的本质又是什么呢?答案就是一个具备iter方法的对象就是可迭代对象,话不多说,上代码看一下

    from collections import Iterable
    class MyIter():
        def __init__(self):
            pass

        def __iter__(self):
            pass

    if __name__ == "__main__":
        test = MyIter()
        print(isinstance(test, Iterable))
    # 输出结果
    True   

     上面说过迭代器能够记住可迭代对象属性的位置,其本质是在可迭代对象中加了next()方法(python2中是next()方法),下面手动实现一个迭代器:

    class MyIterator():
        def __init__(self):
            self.test_list = ['Hello', 'I', 'Love', 'python']
            self.position = 0

        def __iter__(self):
            # 要返回一个迭代器,这里返回自身
            return self
        
        def __next__(self):
            # 该方法的主要作用记录迭代输出位置
            if self.position < len(self.test_list):
                ret = self.test_list[self.position]
                self.position += 1
                return ret
            # 当超出列表长度的时候,抛出停止迭代的异常
            else:
                raise StopIteration

    if __name__ == "__main__":
        test = MyIterator()
        for i in test:
            print(i, end=' ')
    # 输出结果
    Hello I Love python

     总结一下,判断一个对象是不是可迭代的,就看该对象有没有实现iter()方法,要判断一个对象是不是迭代器,就看该对象有没有实现iter()方法和next()方法。我们平时用的比较多的for .. in ..循环,其执行过程的本质就是通过iter()获取一个可迭代对象,然后通过该调用对象的next()方法获取下一个值,知道满足停止迭代的条件之后,抛出停止迭代的异常,然后停止循环。

生成器

     生成器(generator)是一种的特殊的迭代器,其特殊之处在于我们可以根据需要生成数据,生成器的创建方式有两种,第一种比较简单,将列表推导式的[]换成()就是生成器.

    In [3]: a = [x**2 for x in range(5)]
    In [4]: a
    Out[4]: [0, 1, 4, 9, 16]

    In [5]: b = (x**2 for x in range(5))
    In [6]: b # b是一个生成器对象
    Out[6]: <generator object <genexpr> at 0x000001E45FF01B88>
    # 生成器具有__next__()方法
    In [7]: b.__next__()
    Out[7]: 0
    In [8]: b.__next__()
    Out[8]: 1

     第二种方式就是通过使用yield创建生成器,一个函数,只要里面含有yield,那么它一定就是生成器,下面写个简单的代码说明一下通过yield创建的生成器。

    def my_generator():
        i = 0
        print("1111111")
        while i<5:
            yield i
            i += 1
            print("22222")
        print("33333")

    if __name__ == "__main__":
        gg = my_generator()
        print(gg)
        # 输出 <generator object my_generator at 0x0000023DECA9BA20>
        print(gg.__next__())
        # 1111111
        # 0
        print(gg.__next__())
        # 22222
        # 1
        print(gg.__next__())
        # 22222
        # 2

     简述一下上面代码执行的流程:函数执行到第一步(gg=my_generator())的时候,python编译器发现函数中有yield,将这个函数定义为一个生成器,于是实例化gg为一个生成器对象,上面说过,生成器是一个特殊的迭代器,通过第一次调用next()方法,函数从上往下执行,当执行到yield的时候,将yield 后的对象(0)返回,当第二次调用next()方法的时候,会接着yield i继续往下执行,所以先打印22222,执行到yield的时候,将yield 后的对象(1)返回,以此类推。
     讲到yield,总避免不了一个话题,那就是yield与return的对比,二者都能结束函数的运行,他们的区别主要是return 之后,函数下次执行只能从头开始执行,而yield 能够接着继续执行,这就是二者主要区别。

文章转自 https://www.cnblogs.com/study-666/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容