让代码变简洁之python的生成器初识

文章Python中的for循环的进阶理解中,理解了迭代器的基础知识和工作原理。可以看到迭代器的使用,可以简化很多编程工作。

但是自己写迭代器需要实现迭代器协议,也就是说需要自己实现__next__,__iter__等函数,这样使用的时候,也有很多不便捷的地方。为了更简单的生成迭代器,python又引入了一个概念,叫做生成器。可以简单的理解这两者之间的关系,生成器就是生成迭代器的。为了定义生成器,引入了yield关键字。为什么要引入生成器,引入一个新的yield关键字,背后的故事,在后续的文章中有介绍。本文主要介绍一下用生成器如何简化迭代器的实现,简化代码的编程,让程序猿在做某些类别的工作需要使用到迭代器时,不用辛苦的去做一些繁琐的next,iter迭代器协议实现的工作。

下面来看一个实际的例子,这个例子很简单,主要就是输入一个任意n值,程序对1+2+…+n进行求和。

最直接的方法是构建一个list,包含所有1到n的整数,再调用sum函数来进行求和。

sum函数是一个内置函数,接收可迭代对象作为输入参数,对所有接迭代对象中的各个元素进行累加求和。

这个实现很简单直接,能够达到累加的目的,但是问题是需要消耗比较多的内存,因为需要实现将所有的需要相加的元素放到一个list对象中。n越大,需要消耗的内存越大,比较占用空间。如何能够减少内存需要呢?

对于这个特定问题,可以不需要一个可迭代对象存在,直接生成元素的同时进行累加即可,但是我们不考虑这个方法,还是尽量使用sum来进行累加,这样更加通用。

那么需要考虑构建一个可以自己改变的可迭代对象,可以考虑如下的方式建立一个可迭代对象:

这个例子中,可以看到sum在每次进行相加的时候,就会调用first_n的__next__函数来返回当前需要的数据,一旦超过n,抛出StopIteration异常,sum捕获该异常,并且返回最终累计相加结果。

这个实现上,可以很明显看到好处,就是不用实现生成所有的元素的list,每次可以在需要用到数据的时候再生成数据,这样可以减少内存的消耗,同时这样的实现策略也可以在很多情况下,减少不用的数据生成。

有没有更加简单的方式来实现上面的可迭代对象,而不用编程人员自己实现相关的迭代协议函数。由于这样的编程模式很有用,因此python引入的生成器的概念。直接上代码如下:

在这个实现就简洁很多了。生成器的定义和普通函数定义很类似,只是在函数中有yield关键字。python中定义,如果函数定义中出现yield,该函数就被当成生成器来对待。那么函数调用就不再执行具体函数指令,而只是返回一个生成器(也是一个迭代器),每次调用next,就执行函数具体代码,碰到yield的作用就是返回yield后续表达式的值,并且记录下当前的运行位置,停止运行,下次外部调用该生成器的next,可以从记录的返回点的下一句开始运行直到遇到下一个yield、return或者抛出异常等。




转载请注明:http://www.aierlanlan.com/cyrz/3738.html