生成器generator 在Python中3,这种一边循环一边计算的机制,称为生成器,生成器保存的是算法,能节省大量的空间.生成器只记录当前位置,只有一个next()方法。
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
简单生成器:
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
L = [x * x for x in range(10)] G = (x * x for x in range(10)) print(L) >>>[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] print(G) >>><generator object <genexpr> at 0x0000027BFE39C4F8>
创建L和G的区别仅在于最外层的[]和(),L是一个list,而G是一个generator。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
可以使用next(G),计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。正确的方法是使用for循环,因为generator也是可迭代对象.
带yield 语句的生成器
下面代码可以从第一个元素开始,通过相应的算法推算出后续任意的元素,这种逻辑其实非常类似generator。
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 G=fib(6) print(G)
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
加强的生成器
一些加强特性加入到生成器中,所以除了 next()来获得下个生成的值,用户可以将值回送给生成器[send()],在生成器中抛出异常,以及要求生成器退出[close()]。
def gen(x): count = x while True: val = (yield count) if val is not None: count = val else: count += 1 G = gen(5) print (next(G)) >>>5 print (next(G)) >>>6 print (next(G)) >>>7 print('====================') print (G.send(9))#发送数字9给生成器 >>>9 print (next(G)) >>>10 print (next(G)) >>>11 G.close()
通过send()可以把数据9发送给生成器,当生成器close后,就不能再对生成器进行操作了。