python中的迭代器和生成器学习笔记总结

生成器就是一个在行为上和迭代器很是相似的对象.   是个对象!python

迭代,顾名思意就是不停的代换的意思,迭代是重复反馈过程的活动,其目的一般是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代获得的结果会做为下一次迭代的初始值。算法

 

迭代器就是用于迭代操做(for 循环)的对象。它像列表同样能够迭代获取其中的每个元素,任何实现了 __next__ 方法(python2 是 next)的对象均可以称为迭代器。express

 

它与列表的区别在于,构建迭代器的时候,不像列表把全部元素一次性加载到内存,而是以一种延迟计算(lazy evaluation)方式返回元素,这正是它的优势。好比列表含有中一千万个整数,须要占超过400M的内存,而迭代器只须要几十个字节的空间。由于它并无把全部元素装载到内存中,而是等到调用 next 方法时候才返回该元素(call by need 的方式),本质上 for 循环就是不断地调用迭代器的next方法。函数

 

咱们自定义一个迭代器,以斐波那契数列为例:lua

 

class Fib:spa

    def __init__(self):对象

        self.prev = 0内存

        self.curr = 1ci

    def __iter__(self):generator

        return self

    def __next__(self):

        value = self.curr

        self.curr += self.prev

        self.prev = value

        return value

>>> f = Fib()

>>> list(islice(f, 0, 10))

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Fib既是一个可迭代对象(由于它实现了 __iter__方法)又是一个迭代器(由于实现了 __next__方法)。实例变量 prev和 curr用户维护迭代器内部的状态。每次调用 next()方法的时候作两件事:

 

为下一次调用 next()方法修改状态

为当前此次调用生成返回结果

 

迭代器就像一个懒加载的工厂,等到有人须要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。

 

生成器

然而在 Python 中还有一种函数,用关键字 yield 来返回值,这种函数叫生成器函数,函数被调用时会返回一个生成器对象,生成器本质上仍是一个迭代器,也是用在迭代操做中,所以它有和迭代器同样的特性,惟一的区别在于实现方式上不同,后者更加简洁

关系

生成器实际上是一种特殊的迭代器,不过这种迭代器更加优雅。它不须要再像上面的类同样写 __iter__()和 __next__()方法了,只须要一个 yiled关键字。 生成器必定是迭代器(反之不成立),所以任何生成器也是以一种懒加载的模式生成值

 

 

若是列表元素能够按照某种算法推算出来,那咱们是否能够在循环的过程当中不断推算出后续的元素呢?这样就没必要建立完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)

 

要建立一个generator,有不少种方法:

 

 

1.只要把一个列表生成式的[]改为(),就建立了一个generator,又叫生成器表达式(generator expression)

 

>>> L = [x * x for x in range(10)]

>>> L

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> g = (x * x for x in range(10))

>>> g

<generator object <genexpr> at 0x104feab40>

 

打印出generator的每个元素呢?

 

若是要一个一个打印出来,能够经过generator的next()方法:

>>> g.next()

0

>>> g.next()

1

>>> g.next()

4

......

 

不断调用next()方法实在是太变态了,正确的方法是使用for循环,由于generator也是可迭代对象

>>> g = (x * x for x in range(10))

>>> for n in g:

...     print n

...

0

1

4

9

16

25

36

49

64

81

 

2.若是推算的算法比较复杂,能够用函数来实现

 

若是一个函数定义中包含yield关键字,那么这个函数就再也不是一个普通函数,而是一个generator.

 

最难理解的就是generator和函数的执行流程不同。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

 

举个简单的例子(很清楚了),定义一个generator,依次返回数字1,3,5:

 

>>> def odd():

...     print 'step 1'

...     yield 1

...     print 'step 2'

...     yield 3

...     print 'step 3'

...     yield 5

...

>>> o = odd()  ----->返回的是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用next的时候才会真正执行里面的代码。

>>> o.next()

step 1

1

>>> o.next()

step 2

3

>>> o.next()

step 3

5

>>> o.next()

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

StopIteration

 

能够看到,odd不是普通函数,而是generator,在执行过程当中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield能够执行了,因此,第4次调用nex

 

把函数改为generator后,咱们基本上历来不会用next()来调用它,而是直接使用for循环来迭代:

 

例子:好比,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数均可由前两个数相加获得:

 

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

 

(1)用函数实现:

 

def fib(max):

    n, a, b = 0, 0, 1

    while n < max:

        print b

        a, b = b, a + b

        n = n + 1

 

 

输出:

>>> fib(6)

1

1

2

3

5

8

 

(2)要把fib函数变成generator,只须要把print b改成yield b就能够了:

 

 

def fib(max):

    n, a, b = 0, 0, 1

    while n < max:

        yield b

        a, b = b, a + b

        n = n + 1

 

输出:

>>> fib(6)

<generator object fib at 0x104feaaa0>

 

把函数改为generator后,咱们基本上历来不会用next()来调用它,而是直接使用for循环来迭代:

 

>>> for n in fib(6):

...     print n

...

1

1

2

3

5

8

相关文章
相关标签/搜索