早在我们讨论迭代器背后的有效性以及它们如何给非序列元素对象一个像序列的迭代器接口。这很容易明白因为他们有一个方法可以获得下一个元素next()
然而,除非你实现一个迭代器的类,迭代器真正的并没有那么聪明。难道调用函数还没有强大到在迭代中以某种方式生成下一个值并且返回和next()调用一样简单的东西?那就是生成器的动机之一。
生成器的另外一个方面甚至更加强力,。。。协同程序的概念。协同程序是可以运行的独立函数调用,可以暂停或者挂起,并从程序离开的地方继续或者重新开始。在有调用者和(被调用的)协同程序也有通信。举例来说,当协同程序暂停时,我们仍可以从其中获得一个中间的返回值,当调用回到程序中时,能够传入额外或者改变了的参数,但是仍然能够从我们上次离开的地方继续,并且所有状态完整。挂起返回出中间值并多次继续的协同程序被称为生成器,那就是python的生成真正在做的事情。这些提升让生成器更加接近一个完全的协同程序,因为允许值(和异常)能传回到一个继续的函数中,同样的,当等待一个生成器的时候,生成器现在能返回控制,在调用的生成器能挂起(返回一个结果)之前,调用生成器返回一个结果而不是阻塞的等待那个结果返回,让我们更进一步观察生成器自顶向下的启动。
什么是python的生成器?从语法上讲,生成器是一个带yield的语句的函数,一个函数或者子程序只返回一次,但一个生成器能暂停执行并返回一个中间的结果---那就是yield语句的功能,返回一个值给调用者并暂停执行,当生成器的next()方法被调用的时候,他会准确的从离开地方继续。
简单的生成器特性:
与迭代器相似,生成器以另外的方式运作:当达到一个真正的返回或者函数结束没有更多的值返回(当调用next()),一个StopIteration异常就会抛出。下面是一个简单的生成器:
>>> def simpleGen(): yield 1 yield '2---->punch!' >>> #现在我们有自己的生成器,让我们调用他们来获得和保存一个生成器对象(以便我们能够调用它的next()方法从这个对象中获得连续的中间值) >>> myG = simpleGen() >>> myG.next() 1 >>> myG.next() '2---->punch!' >>> myG.next() Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> myG.next() StopIteration >>> #由于python的for循环有next()调用和对StopIteration的处理,使用一个for循环而不是手动迭代穿过一个生成器(或者那种事物的迭代器)总是很简洁的 >>> for eachItem in simpleGen(): print eachItem 1 2---->punch! >>>当然这个例子挺傻的!为什么不对这使用真正的迭代器呢?许多动机源自能够迭代穿越序列,而这需要函数威力而不是已经在某个序列中静态对象。
在接下来的例子中,我们将要创建一个带序列并从那个序列中返回一个随机元素的随机迭代器:
>>> from random import randint >>> def randGen(aList): while len(aList) > 0: yield aList.pop(randint(0,len(aList))) >>> #不同点在于每个返回的元素将从哪个队列中消失,像一个list.pop()和random.choice()的结合的归类 >>> for item in randGen(['rock','paper','scissors']): print item scissors rock paper >>>这些简单的例子应该让我们有点明白生成器是如何工作的,但你或许会问。“在我的应用中,我可以在哪使用生成器?”;“或许你会问最适合使用这些强大的构建的地方在哪里呀?”
使用生成器的最好地方就是当你正迭代穿越一个巨大的数据集合时,而重复迭代这个数据集合是一个很麻烦的事情,比如一个巨大的磁盘文件,或者一个复杂的数据库查询,对于每行的数据,你希望执行非元素的操作以及处理,但当正指向和迭代过他的时候,你“不想失去你的地盘”。
你要想抓取一块数据,状态在挂起和再继续的过程中是保留了的,所以你会觉得很舒服有一个安全的处理数据的环境。没有生成器的话,你的程序代码很有可能会有很长的函数,里面有一个很长的循环,当然,这仅仅是因为一个语言这样的特征,不意味着你需要用他。如果在你的程序里没有明显合适的话,那就别增加多余的复杂性!当你遇到合适的情况时,你便会知道什么时候使用生成器正是需要的东西。!