来源:python中国网 时间:2019-06-06

  一、什么是生成器

  通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,会很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

  二、创建生成器

  1.把列表生成式的[]改成()

# -*- coding: utf-8 -*-

L = [ x*2 for x in range(5)]
G = ( x*2 for x in range(5))
print(type(L))
print(type(G))
print(G.__next__())
print(G.__next__())
print(G.__next__())
print(G.__next__())
print(G.__next__())


D:python3installpython.exe D:/python/py3script/python66.py


0
2
4
6
8

Process finished with exit code 0


  2.函数内部yield配合__next__()函数

  著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:1,1,2,3,5,8,13,21,34,...

# -*- coding: utf-8 -*-
def gen(times):
    n = 0
    a, b = 0, 1
    while n < times:
        yield b
        a, b = b, a + b
        n += 1
res = gen(5)
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())


D:python3installpython.exe D:/python/py3script/python66.py
1
1
2
3
5

Process finished with exit code 0

  3.函数内部yield配合send()函数

# -*- coding: utf-8 -*-


def MyGenerator():
    value = (yield 1)
    value = (yield value)


gen = MyGenerator()
print(gen.__next__())
print(gen.send(2))
print(gen.send(3))

D:python3installpython.exe D:/python/py3script/python66.py
Traceback (most recent call last):
1
  File "D:/python/py3script/python66.py", line 12, in 
2
    print(gen.send(3))
StopIteration

Process finished with exit code 1


  上面代码的运行过程如下:

  当调用gen.next()方法时,python首先会执行MyGenerator方法的yield1语句。由于是一个yield语句,因此方法的执行过程被挂起,而next方法返回值为yield关键字后面表达式的值,即为1。

  当调用gen.send(2)方法时,python首先恢复MyGenerator方法的运行环境。同时,将表达式(yield1)的返回值定义为send方法参数的值,即为2。这样,接下来value=(yield1)这一赋值语句会将value的值置为2。继续运行会遇到yieldvalue语句。因此,MyGenerator方法再次被挂起。同时,send方法的返回值为yield关键字后面表达式的值,也即value的值,为2。

  当调用send(3)方法时MyGenerator方法的运行环境。同时,将表达式(yieldvalue)的返回值定义为send方法参数的值,即为3。这样,接下来value=(yieldvalue)这一赋值语句会将value的值置为3。继续运行,MyGenerator方法执行完毕,故而抛出StopIteration异常。

  生成器总结:

  生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第n次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。

  生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。

  生成器特点:

  1.节约内存

  2.迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的