看到下面这段程序的时候,有点不明白这个yield到底是个啥东西,看了网上很多的博客,大致理解了yield的含义,所以记录下来。
要说yield首先要说python中的生成器,那么什么是生成器?
假设有一个包含一百万个元素的列表,我们每次只需要这个列表中的一个元素。如果程序一开始就创建该列表的话,无疑要浪费大量存储空间。所以如果列表中的元素能够以某种方法推导出来,我们就可以根据程序的需要对列表元素进行生成,进而节省大量存储空间。这种一边循环一遍计算的机制,就是生成器。
那么yield和生成器有什么关系?
如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是变成了一个生成器。
此时a是一个函数,而a()则是一个生成器对象。我们用下面的程序来看看yield函数到底怎么用。
程序开始时,初始化一个生成器a=a(0),然后通过next方法循环打印生成器中的元素。生成器中的yield其实就相当于一个断点,每次程序执行到一个yield,就将其后的值当做生成器的一个元素。然后再次调用next方法,就从yield之后继续运行,直到运行到程序结束。
从上面的程序来看,首先定义生成器对象a,然后进入主程序循环:
第一次调用next:进入a(i)内部,首先打印‘func a is running’,然后进入生成器循环。打印‘in circle’,表示当前生成器进入循环中,然后碰到yield i。此时将i的值作为第一个元素返回给生成器a,然后结束next方法,打印20个‘=’。
第二次调用next:从上次的yield处继续执行,首先会打印一个‘step 1’,然后i++,然后又是一个yield,此时再次将i的值作为生成器的下一个元素进行返回,然后结束next方法,打印20个‘=’。
第三次调用next:再次从上次的yield处继续执行,首先打印一个‘step 2’,然后注意此时由于i的值为1,满足生成器循环的条件,因此会打印‘in circle’,然后继续向下执行。
第四、五、六次调用next:与上面保持一致。
第七次调用next:从yield处向后执行,会首先打印‘step 2’,然后此时i=3,不满足生成器循环条件,于是打印‘out circle’,然后打印i的值。
第八次调用next:由于此时生成器无下一个元素,程序会抛出StopIteration异常,然后结束整个程序的运行。
最后要说一下python2中的range和xrange。
Python2中,range是一个内建的函数,用于生成一个列表。而xrange则为一个生成器,循环时不断生成元素。Python3时就将内建的range改成了xrange,只不过名字没变。