列表生成式:代码演示:列表生成式list1〔x2forxinrange(10)〕x2处也可以放函数print(list1)〔0,1,4,9,16,25,36,49,64,81〕代码等价于list2〔〕forxinrange(10):list2。append(x2)print(list2)列表生成式生成器: 通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。 要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的〔〕改成(),就创建了一个generator:代码演示:list1(x2forxinrange(10))比较生成器和列表生成式代码演示importtimestarttimetime。time()list1(x2forxinrange(10))stoptimetime。time()print(list1)print(thelist1runtimeiss(stoptimestarttime))starttimetime。time()list2〔x2forxinrange(10)〕stoptimetime。time()print(list2)print(thelist2runtimeiss(stoptimestarttime))运行结果:generatorobjectgenexprat0x0000011FACD1ED60生成器只有一个列表地址,并没有具体的数值thelist1runtimeis0。0〔0,2,4,6,8,10,12,14,16,18〕thelist2runtimeis0。0生成器和生成式的对比生成器只有在调用的时候才会生成相应的数据生成式可以直接打印列表,生成器只能打印地址生成式可以通过下角标获取元素,生成器不行生成器可以通过next()函数获得生成器(generator)的下一个返回值list1(x2forxinrange(100000000))forxinlist1:print(x)list1。nextlist1。nextlist1。next只有一个next()用来记录当前位置,没有方法访问前面的元素,只能往后面走 generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到: 1,1,2,3,5,8,13,21,34,。。。 斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:deffib(sum):a,b,c0,1,0whilecsum:print(b)a,bb,abc1fib(6)斐波那契数列仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yieldb就可以了:deffib(sum):a,b,c0,1,0whilecsum:print(b)yieldb代码执行到这里,会跳出这个函数,并将b的值返回到使用next的代码处a,bb,abc1print(fib(6))这里得到的就是生成器pfib(6)print(next(p))print(next(p))print(做点别的事情)print(next(p))print(p。next())print(next(p))print(p。next())第二种生成器生成方式这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:ffib(6)fgeneratorobjectfibat0x104feaaa0这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。在上面fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:forninfib(6):print(n)但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:deffib(sum):a,b,c0,1,0whilecsum:yieldba,bb,abc1return返回值只能传递给异常gfib(3)whileTrue:try:xnext(g)print(g:,x)exceptStopIterationase:print(Generatorreturnvalue:,e。value)break运行结果:g:1g:1g:2Generatorreturnvalue:返回值只能传递给异常获取返回值的方式 还可通过yield实现在单线程的情况下实现并发运算的效果:next()和next():效果相同,只是使用方式不同,都可以唤醒yield,并接收yield传过来的值。send():也可以唤醒yield,也可以接收yield传递过来的值,而且,还可以在唤醒yield的同时,为yield传递一个值coding:utf8通过生成器实现协程并行运算importtimedefconsumer(name):print(s准备吃包子啦!name)whileTrue:baoziyieldprint(包子〔s〕来了,被〔s〕吃了!(baozi,name))defproducer(name):cconsumer(name)c2consumer(B)c。next()c2。next()print(老子开始准备做包子啦!)foriinrange(10):time。sleep(1)print(做了2个包子!)c。send(i)c2。send(i)