Python yield 使用浅析
初学Python的开发者经常会发现很多Python函数中用到了yield关键字,然而,带有yield的函数执行流程却和普通函数不一样,yield到底用来做什么,为什么要设计yield?本文将由浅入深地讲解yield的概念和用法,帮助读者体会Python里yield简单而强大的功能。
您可能听说过,带有yield的函数在Python中被称之为generator(生成器),何谓generator?
我们先抛开generator,以一个常见的编程题目来展示yield的概念。
如何生成斐波那契數列
斐波那契(Fibonacci)數列是一个非常简单的递归数列,除第一个和第二个数外,任意一个数都可由前两个数相加得到。用计算机程序输出斐波那契數列的前N个数是一个非常简单的问题,许多初学者都可以轻易写出如下函数:
清单1.简单输出斐波那契數列前N个数
deffab(max): n,a,b=0,0,1 whilen<max: printb a,b=b,a+b n=n+1
执行fab(5),我们可以得到如下输出:
>>>fab(5) 1 1 2 3 5
结果没有问题,但有经验的开发者会指出,直接在fab函数中用print打印数字会导致该函数可复用性较差,因为fab函数返回None,其他函数无法获得该函数生成的数列。
要提高fab函数的可复用性,最好不要直接打印出数列,而是返回一个List。以下是fab函数改写后的第二个版本:
清单2.输出斐波那契數列前N个数第二版
deffab(max): n,a,b=0,0,1 L=[] whilen<max: L.append(b) a,b=b,a+b n=n+1 returnL
可以使用如下方式打印出fab函数返回的List:
>>>forninfab(5): ... printn ... 1 1 2 3 5
改写后的fab函数通过返回List能满足复用性的要求,但是更有经验的开发者会指出,该函数在运行中占用的内存会随着参数max的增大而增大,如果要控制内存占用,最好不要用List
来保存中间结果,而是通过iterable对象来迭代。例如,在Python2.x中,代码:
清单3.通过iterable对象来迭代
foriinrange(1000):pass
会导致生成一个1000个元素的List,而代码:
foriinxrange(1000):pass
则不会生成一个1000个元素的List,而是在每次迭代中返回下一个数值,内存空间占用很小。因为xrange不返回List,而是返回一个iterable对象。
利用iterable我们可以把fab函数改写为一个支持iterable的class,以下是第三个版本的Fab:
清单4.第三个版本
classFab(object):
def__init__(self,max): self.max=max self.n,self.a,self.b=0,0,1
def__iter__(self): returnself
defnext(self): ifself.n<self.max: r=self.b self.a,self.b=self.b,self.a+self.b self.n=self.n+1 returnr raiseStopIteration()