Python关键字yield的作用是什么?用来干什么的? 回答: 为了理解什么是 yield,你必须理解什么是生成器。在理解生成器之前,让我们先走近迭代。
可迭代对象
当你建立了一个列表,你可以逐项地读取这个列表,这叫做一个可迭代对象:
>>> mylist = [1, 2, 3]>>> for i in mylist :... print(i)123
mylist 是一个可迭代的对象。当你使用一个列表生成式来建立一个列表的时候,就建立了一个可迭代的对象:
>>> mylist = [x*x for x in range(3)]>>> for i in mylist :... print(i)014
所有你可以使用 for .. in .. 语法的叫做一个迭代器:列表,字符串,文件……你经常使用它们是因为你可以如你所愿的读取其中的元素,但是你把所有的值都存储到了内存中,如果你有大量数据的话这个方式并不是你想要的。
生成器
生成器是可以迭代的,但是你 只可以读取它一次 ,因为它并不把所有的值放在内存中,它是实时地生成数据:
>>> mygenerator = (x*x for x in range(3))>>> for i in mygenerator :... print(i)014
看起来除了把 [] 换成 () 外没什么不同。但是,你不可以再次使用 for i in mygenerator , 因为生成器只能被迭代一次:先计算出0,然后继续计算1,然后计算4,一个跟一个的…
yield关键字
yield 是一个类似 return 的关键字,只是这个函数返回的是个生成器。
>>> def createGenerator() :... mylist = range(3)... for i in mylist :... yield i*i...>>> mygenerator = createGenerator() # create a generator>>> print(mygenerator) # mygenerator is an object!>>> for i in mygenerator:... print(i)014
实例:斐波那契数列
斐波那契数列是一个非常简单的递归数列,除次一个和第二个之外,任意一个数都可由前两个数相加得到,
例一: 初级写法
结果没有问题,直接在 fab 函数中用 print 打印数字会导致该函数可复用性较差,因为 fab 函数返回 None,其他函数无法获得该函数生成的数列(不带有return功能)
def fab(max): n, a, b = 0, 0, 1 while n < max: print b a, b = b, a + b n = n + 1 >>> fab(5) 1 1 2 3 5
例二: 初级写法并且有return功能
该写法弥补了例一的弊端,在犹豫return的是一个list会占用大量的内存.如果要控制内存使用 最好还是不要用list.例似range和xrange的区别
def fab(max): n, a, b = 0, 0, 1 L = [] while n < max: L.append(b) a, b = b, a + b n = n + 1 return L >>> for n in fab(5): ... print n ... 1 1 2 3 5
例三: 类方法编写迭代器
改方法配合使用__iter__和next来达到生成迭代器的方法,但写法过于复杂
class Fab(object): def __init__(self, max): self.max = max self.n, self.a, self.b = 0, 0, 1 def __iter__(self): return self def next(self): if self.n < self.max: r = self.b self.a, self.b = self.b, self.a + self.b self.n = self.n + 1 return r raise StopIteration() >>> for n in Fab(5): ... print n ... 1 1 2 3 5
例四: yield版
和第一版对比仅仅只是把print修改成yield即可.每次执行到yield就return一个生成器.下次再循环时从yiled后面开始执行直到再次执行到yield return一个生成器。
def fab(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 >>> for n in fab(5): ... print n ... 1 1 2 3 5
另一个yield的例子:
def read_file(fpath): BLOCK_SIZE = 1024 with open(fpath, 'rb') as f: while True: block = f.read(BLOCK_SIZE) if block: yield block else: return