美文网首页python基础
15、Python 迭代器与生成器

15、Python 迭代器与生成器

作者: vannesspeng | 来源:发表于2018-12-18 14:12 被阅读0次

    首先让我们来了解一下,可第迭代对象(Iterable)

    可迭代对象

    刚才说过,很多容器都是可迭代对象,此外还有更多的对象同样也是可迭代对象,比如处于打开状态的files,sockets等等。但凡是可以返回一个迭代器的对象都可称之为可迭代对象,听起来可能有点困惑,没关系,先看一个例子:

    >>> x = [1, 2, 3]
    >>> y = iter(x)
    >>> z = iter(x)
    >>> next(y)
    1
    >>> next(y)
    2
    >>> next(z)
    1
    >>> type(x)
    <class 'list'>
    >>> type(y)
    <class 'list_iterator'>
    

    这里x是一个可迭代对象,可迭代对象和容器一样是一种通俗的叫法,并不是指某种具体的数据类型,list是可迭代对象,dict是可迭代对象,set也是可迭代对象。y和z是两个独立的迭代器,迭代器内部持有一个状态,该状态用于记录当前迭代所在的位置,以方便下次迭代的时候获取正确的元素。迭代器有一种具体的迭代器类型,比如list_iterator,set_iterator。可迭代对象实现了iter方法,该方法返回一个迭代器对象。

    当运行代码:

    x = [1, 2, 3]
    for elem in x:
        ...
    

    反编译该段代码,你可以看到解释器显示地调用GET_ITER指令,相当于调用iter(x),FOR_ITER指令就是调用next()方法,不断地获取迭代器中的下一个元素,但是你没法直接从指令中看出来,因为他被解释器优化过了。

    >>> import dis
    >>> x = [1, 2, 3]
    >>> dis.dis('for _ in x: pass')
      1           0 SETUP_LOOP              14 (to 17)
                  3 LOAD_NAME                0 (x)
                  6 GET_ITER
            >>    7 FOR_ITER                 6 (to 16)
                 10 STORE_NAME               1 (_)
                 13 JUMP_ABSOLUTE            7
            >>   16 POP_BLOCK
            >>   17 LOAD_CONST               0 (None)
                 20 RETURN_VALUE
    

    迭代器

    那么什么迭代器呢?它是一个带状态的对象,他能在你调用next()方法的时候返回容器中的下一个值,任何实现了iternext()(python2中实现next())方法的对象都是迭代器,iter返回迭代器自身,next返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常,至于它们到底是如何实现的这并不重要。

    所以,迭代器就是实现了工厂模式的对象,它在你每次你询问要下一个值的时候给你返回。有很多关于迭代器的例子,比如itertools函数返回的都是迭代器对象。

    >>> list = [1,2,3,4]
    >>> it = iter(list)
    >>> print(next(it))
    1
    >>> print(next(it))
    2
    >>> print(next(it))
    3
    >>> print(next(it))
    4
    >>> print(next(it))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    >>>
    

    迭代器对象可以使用常规for语句进行遍历:

    #!/usr/bin/python3
     
    list=[1,2,3,4]
    it = iter(list)    # 创建迭代器对象
    for x in it:
        print (x, end=" ")
    

    执行以上程序,输出结果如下:

    1 2 3 4

    也可以使用 next() 函数:

    #!/usr/bin/python3
     
    import sys         # 引入 sys 模块
     
    list=[1,2,3,4]
    it = iter(list)    # 创建迭代器对象
     
    while True:
        try:
            print (next(it))
        except StopIteration:
            sys.exit()
    

    执行结果如下:

    1
    2
    3
    4

    把一个类作为一个迭代器使用需要在类中实现两个方法 iter() 与 next() 。
    如果你已经了解的面向对象编程,就知道类都有一个构造函数,Python 的构造函数为 init(), 它会在对象初始化的时候执行。iter() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。next() 方法(Python 2 里是 next())会返回下一个迭代器对象。
    创建一个返回数字的迭代器,初始值为 1,逐步递增 1:

    class MyNumbers:
      def __iter__(self):
        self.a = 1
        return self
     
      def __next__(self):
        x = self.a
        self.a += 1
        return x
     
    myclass = MyNumbers()
    myiter = iter(myclass)
     
    print(next(myiter))
    print(next(myiter))
    print(next(myiter))
    print(next(myiter))
    print(next(myiter))
    

    执行结果如下:

    1
    2
    3
    4
    5

    StopIteration
    StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
    在 20 次迭代后停止执行:

    class MyNumbers:
      def __iter__(self):
        self.a = 1
        return self
     
      def __next__(self):
        if self.a <= 20:
          x = self.a
          self.a += 1
          return x
        else:
          raise StopIteration
     
    myclass = MyNumbers()
    myiter = iter(myclass)
     
    for x in myiter:
      print(x)
    

    执行结果如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    生成器

    生成器是Python语言中最吸引人的特性之一,生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要再像上面的类一样写iter()和next()方法了,只需要一个yiled关键字。 生成器一定是迭代器(反之不成立),因此任何生成器也是以一种懒加载的模式生成值。
    在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
    调用一个生成器函数,返回的是一个迭代器对象。
    以下实例使用 yield 实现斐波那契数列:

    #!/usr/bin/python3
     
    import sys
     
    def fibonacci(n): # 生成器函数 - 斐波那契
        a, b, counter = 0, 1, 0
        while True:
            if (counter > n): 
                return
            yield a
            a, b = b, a + b
            counter += 1
    f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
     
    while True:
        try:
            print (next(f), end=" ")
        except StopIteration:
            sys.exit()
    

    执行以上程序,输出结果如下:

    0 1 1 2 3 5 8 13 21 34 55

    生成器表达式(generator expression)

    生成器表达式是列表推倒式的生成器版本,看起来像列表推导式,但是它返回的是一个生成器对象而不是列表对象。

    >>> a = (x*x for x in range(10))
    >>> a
    <generator object <genexpr> at 0x401f08>
    >>> sum(a)
    285
    1
    2
    3
    4
    5
    >>> a = (x*x for x in range(10))
    >>> a
    <generator object <genexpr> at 0x401f08>
    >>> sum(a)
    285
    

    总结

    • 容器是一系列元素的集合,str、list、set、dict、file、sockets对象都可以看作是容器,容器都可以被迭代(用在for,while等语句中),因此他们被称为可迭代对象。
    • 可迭代对象实现了iter方法,该方法返回一个迭代器对象。
    • 迭代器持有一个内部状态的字段,用于记录下次迭代返回值,它实现了nextiter方法,迭代器不会一次性把所有元素加载到内存,而是需要的时候才生成返回结果。
    • 生成器是一种特殊的迭代器,它的返回值不是通过return而是用yield。

    相关文章

      网友评论

        本文标题:15、Python 迭代器与生成器

        本文链接:https://www.haomeiwen.com/subject/htoqkqtx.html