为什么要使用生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。比如创建一个100万个元素的列表,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
什么是生成器
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
创建生成器
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
1 # The author is tou 2 # Version:Python 3.6.55 3 print([i*2 for i in range(10)]) #列表生成式 4 5 b = (i*2 for i in range(10)) #生成器 6 # print(b.__next__()) 7 # print(b.__next__()) #__next__()方法取下一个值 8 for i in b: #亦可迭代取值 9 print(i)
创建L
和g
的区别仅在于最外层的[]
和()
,L
是一个list,而g
是一个generator。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
如果要一个一个打印出来,可以通过__next__()方法
获得generator的下一个返回值
generator保存的是算法,每次调用__next__()
,就计算出
g
的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。
当然,上面这种不断调用next(g)
实在是太变态了,正确的方法是使用for
循环,因为generator也是可迭代对象。
实现斐波拉契数列(Fibonacci):
除第一个和第二个数外,任意一个数都可由前两个数相加得到。
1 def fib3(max): #加入生成器的斐波那契数列,并抓取超过次数所报错误,并返回return值 2 n,a,b = 0,0,1 3 while n<max: 4 yield b 5 a,b = b,a+b 6 n = n+1 7 return "done" 8 #print(fib(10)) #直接打印 9 f = fib3(10) 10 while True: 11 try: 12 print("f.__next__():",f.__next__()) 13 except StopIteration as e: #抓取超过次数所报错误 14 print("Generator return value :",e.value) 15 break
这就是定义generator的另一种方法。如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator。
1 # The author is tou 2 # Version:Python 3.6.5 3 import time 4 5 def consumer(name): #消费者 6 print("%s准备吃包子啦!"%name) 7 while True: 8 baozi = yield 9 print("第[%s]份包子来了,被[%s]吃了!"%(baozi,name)) 10 # c = consumer("tou") 11 # c.__next__() 12 # b1 = "韭菜馅" 13 # c.send(b1) #给yield赋值 14 15 def producer(name): #生产者 16 c = consumer("toutou") 17 c.__next__() 18 print("%s开始准备做包子了!"%name) 19 for i in range(10): 20 time.sleep(1) 21 print("%s做了两个包子!"%name) 22 c.send(i+1) 23 producer("zhuozhuo")
请发表评论