1。什么是发电机?在介绍生成器之前,我们可以回忆一下python中函数是如何调用的。对于普通的函数调用,函数会立即执行,直到函数中出现return关键字或者执行完最后一行。明明是生成器,为什么要提函数呢?这是因为大多数时间生成器都是作为函数实现的。普通函数:返回一个值给调用者,返回值给调用者后,函数就消亡了,也就是销毁了。生成器函数:yield("give")一个值给调用者,yield("give")一个值后,函数还活着,调用者接着会生成第二个值,第三个值,第四个值。..无法阅读?没关系,我们来看下面的例子。编程源于生活:神奇的包子铺。王大爷在楼下开了一家包子店。别小看这家包子店。这家包子店里有两个神奇的蒸笼。只需将蒸笼放在蒸架上即可。自己做包子。小A和小B同时吃包子。小B点了50个馒头,王大爷用神奇的蒸笼一次性给小B蒸了50个馒头,50个馒头装在50个小碗里。撤掉了,于是小B开始坐下来吃包子。小A也买了50个包子,但是他跟王叔叔说,你把我的包子放在蒸笼里,我一次只吃一个。于是王叔给了小A一个小碗,每次小A吃完一个包子,他就去蒸笼里拿一个包子,等小A打开蒸笼,一个包子就出来给他吃了。其中:小A:生成器函数调用者小B:普通函数调用者小A的蒸笼:生成器函数(小A拿了一个包子后,继续放在蒸架上准备剩下的49个包子,函数保持状态,可以记录拿了多少,还剩多少)小B的蒸笼:普通功能(给小B拿了50个包子,直接被王叔收起来,功能被破坏了,我还想去吃包子,王大爷需要把蒸笼放回蒸架上)小A吃1小碗:占内存大小(1KB)小B吃50小碗:占内存大小(50KB)王大爷:CPUdefsimple_generator():x=1yieldgenrator=simple_generator()#如果函数中使用yield关键字,将返回一个生成器对象print(type(genrator))#和往常一样,看看是什么在生成器对象中print(dir(genrator))[...,'__iter__','__next__'...]#Saw又是我们的老朋友了。..迭代器中的两兄弟到这里能得出什么结论呢?生成器也是迭代器,它的功能类似于迭代器。生成器是一种特殊的迭代器。对迭代器不熟悉的朋友可以看看我之前的文章。2.创建生成器2.1使用yield关键字defsimple_generator():x=1yieldx#第一次调用next(),到此为止,返回xgenrator=simple_generator()print(genrator)#print(type(genrator))#如果一个函数定义中包含了yield关键字,那么这个函数就不再是一个普通的函数,而是一个生成器(generator)2.2生成器表达式generator=(iforiinrange(10))print(generator)listcomprehension的[]可以改为()来创建生成器。那么生成器和列表有什么区别呢?举个很直观的例子_list=[iforiinrange(10)]print("取出第一个包子:",_list[0])#取出第一个包子:0print("取出第二个包子:",_list[1])#取出第二个馒头:1print("取出第三个馒头:",_list[2])#取出第三个馒头:2foriin_list:print("取出序号thebun:",i)馒头编号:0馒头编号:1馒头编号:2馒头编号:3馒头编号:4馒头编号:5馒头编号:6馒头包子序号:7馒头序号:8取出包子序号:9generator=(iforiinrange(10))print("取出第一个包子:",next(generator))#取出第一个包子第一个包子:0print("取出第二个包子:",next(generator))#取出第二个包子:1print("取出第三个包子:",next(generator))#取出第三个包子:2foriingenerator:print("取出包子序号:",i)取出包子序号number:3#这个和list有点不同,list每次都是从0开始,生成器只能从当前数中取出包子编号:4取出包子编号:5取出包子编号:6取出包子编号:7取出包子编号:8取出包子编号:9对比上面的包子店:小B一次拿了50个包子,把每个包子放在一个碗里面,假设碗里有编号,那么小B可以随便拿一个编号的包子,小B可以随意排列组合包子,小B也很喜欢数包子,小B不停地数着自己有多少个包子,反复数着都是可以的,但是小A的情况就不一样了。他只有一个碗,一次只能盛一个。拿了1号包子之后,如果他想得到2号包子,就必须把1号包子扔掉或者吃掉。小A还不会数包子,只能记录自己已经拿了多少个包子小B按个数随机拿包子:列表索引的值小A拿第一个包子再拿第二个:值小B通过next()函数取到2号后,不能再取到1号(1号已经丢/吃):生成器只能执行一次小B号包子,可以重复多次,而且每次都可以从1开始数:for...in...可以多次使用,每次都从index0开始数。小A只能从当前获得的包子开始数。一旦计数结束,就没有办法再计数了:for...in..。它只能使用一次。当得到当前的数后,从这个数开始遍历。结论:生成器保存了算法。每次调用next()时,都会计算下一个元素的值,直到计算完最后一个元素,当没有更多元素时,将抛出StopIteration错误。3.yield关键字yield关键字是一个比较抽象的概念。还是包子店:王大爷把蒸笼放到蒸笼架上,开始蒸包子。每次小A打开蒸笼的盖子,蒸笼都会原地夹一个馒头给他,然后自动合上盖子。王大爷:CPU蒸笼:生成器函数蒸笼架:内存空间蒸笼上架:加载函数打开蒸笼盖:执行next()方法给小A送馒头:yield(生成)一个值给调用者关闭蒸笼盖:函数exit(也可以理解为暂停)下次开盖:再次执行next()方法,从上次yield的地方开始,遇到下一次yield时退出defmake_baozi(xx):returnxxdefsimple_generator():print("这是第一次做猪肉白菜包子")formulation="pork,cabbage"x_zhu=make_baozi(formulation)yieldx_zhu#第一次开盖,成品返回猪肉白菜包子给你。暂停,等你吃完,记录我给你猪肉白菜包子#上面的block下次不会执行,而是从这个关键字开始执行print("第二次做叉烧包子buns")formulation="CharSiew"x_cha=make_baozi(formulation)yieldx_cha#第二次开盖,把猪肉包还给你,暂停,等你吃完,记录我做了猪肉还有白菜包子、叉烧包子给你print("第三次做玉米馅包子")formulation="corn"x_yu=make_baozi(formulation)yieldx_yu#第一次开盖,然后把做好的玉米包子还给你,暂停,等你吃完了,记录我给你猪肉白菜包子,叉烧包子,玉米包子))#第二次拿了包子withcharsiu#charsiuprint(next(genrator))#第三次拿包子withcornfilling#Cornprint(next(genrator))Traceback(mostrecentcalllast):File"/app/util-python/test.py",line36,inprint(next(genrator))StopIteration我们可以把yield理解为函数的暂停键,next()函数就是开始键。挂起的同时,价值也会返还给你。下次启动时,会从上次暂停的地方继续执行。3.1yieldfrom在Python3.3版本的PEP380中加入了yieldfrom语法。yieldfrom可以直接返回可迭代对象中的每个数据作为生成器的结果defsimple_generator():a=[1,2,3]yieldagenrator=simple_generator()print(genrator.__next__())#[1,2.3]defsimple_generator():a=[1,2,3]yieldfromagenrator=simple_generator()print(genrator.__next__())#1print(genrator.__next__())#2print(genrator.__next__())#34.生成器方法生成器是迭代器的一种,生成器比迭代器多了三个方法:send()、close()、throw()4.1send当生成器处于暂停状态时,send传递一个值给generatordefsimple_generator():a="test"a=yieldayieldagenrator=simple_generator()print(genrator.send("dd"))Traceback(最近调用last):文件"/app/util-python/test.py",line12,ingenerator.send("dd")TypeError:can'tsendnon-Nonevaluetoajust-startedgenerator上面的用法报错,为什么?因为此时我们的发电机还没有启动,所以我们需要先启动发电机。方法一启动生成器:print(genrator.send(None))方法二启动生成器:print(genrator.__next__())生成器启动后重试:defsimple_generator():a="test"#第一次启动会在这里执行。暂停之后,可以在这里给这个关键字发送参数a=yieldaprint("下次要执行的代码块")yieldagenrator=simple_generator()print(genrator.__next__())#打印:testprint(genrator.send("newvalue"))#Print:newvalue总结一下:这个方法可以给生成器发送一个参数,但是必须先启动生成器,也就是必须执行到第一个yield关键字,并且然后停在这个关键字上。此时按下暂停键的yield可以接受外部发送的值4.2throw()在生成器函数defsimple_generator()执行暂停时抛出指定异常:a="startexecution"try:yieldaexceptValueError:print("Thethrowedexceptionwascatched")b="Executethesecondyield"yieldbgenrator=simple_generator()print(genrator.__next__())#执行到yielda,所以应该打印在这里:开始执行print(genrator.throw(ValueError))#从yielda开始执行,抛出ValueError异常。如果处理完抛出的异常,那么会继续执行到yieldb,否则直接抛出异常,程序停止#所以这里的结果应该是打印出来的:执行第二次yield,可以和下面的代码对比一下看看,应该可以加深理解defsimple_generator():a="开始执行"try:yieldaraiseValueErrorexceptValueError:print("抛出的异常被捕获")b="第二个yield是关于tobeexecuted"yieldbgenrator=simple_generator()print(genrator.__next__())#执行到yielda,所以这里应该是打印:starttoexecuteprint(genrator.__next__())#从yielda开始执行,执行raiseValueError抛出ValueError异常,然后执行到yieldb4.3close()throwsageneratorGeneratorExit异常,表示generator生命周期结束。defsimple_generator():a="开始执行"try:yieldaexceptValueError:print("抛出的异常被捕获")exceptGeneratorExit:print("Generatorexited")b="执行第二次yield"yieldbgenrator=simple_generator()print(genrator.__next__())genrator.close()上面代码的最终结果:Traceback(最近一次调用last):文件“/app/util-python/test.py”,第23行,在generator.close()RuntimeError:generatorignoredGeneratorExit因为generator已经执行了generator.close()方法,抛出GeneratorExit异常,generator方法后面执行的语句不能有yield语句,否则会出现RuntimeError会生成,所以这里需要去掉下面两行代码。或者先执行generator.__next__()再执行generator.close(),让函数执行完所有的yield再closeb="Executethesecondyield"yieldb5。实现斐波那契数列(Fibonacci)deffib(max):n=0a=0b=1whilen