关于生成器yield关键字
带有 yield 的函数在 Python 中被称之为 generator(生成器)
那么什么是生成器呢?
看看下面这个例子:
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
当我们使用list(列表)等 iterable(可迭代对象)类型来保存中间信息时,创建列表只是一个中间过程的时候,为了避免创建庞大的列表(如上代码片中该函数参数不断增大,内存的占用也会越多),这时就可以使用生成器表达式来完成。
所以我们对上面函数进行优化:
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()
在这个类中,我们使用 iterable 将 fab 函数改写为一个支持 iterable 的 class。Fab 类通过 next() 方法不断返回数列的下一个数,内存占用始终为常数。拥有“next”方法的对象都是一个迭代器。可是我们使用了自定义容器,代码量却很多所以就需要用到生成器,yield关键字。
再次优化:
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
# print b
a, b = b, a + b
n = n + 1
使用该函数:
>>> for n in fab(5):
... print n
...
1
1
2
3
5
当我们使用yield关键字时,fab()就成为了一个 generator(生成器),首先函数for循环,循环时先返回yield关键字后面的值(b)
当下一循环时,就会从yield关键字下一行开始运行即(a,b = b, a+b)执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。大家可以使用pycharm的调试模式,设置断点,实验一下。
总结
- yield 是一个类似 return 的关键字,迭代一次遇到yield时就返回yield后面(右边)的值。重点是:下一次迭代时,从上一次迭代遇到的yield后面的代码(下一行)开始执行。
- yield就是return 返回一个值,并且记住这个返回的位置,下次迭代就从这个位置后(下一行)开始。
示例
def yield_test(n):
for i in range(n):
yield call(i)
print('i=',i)
print('do something')
print('end')
def call(i):
return i+2
for i in yield_test(5):
print(i,',')
请大家设置断点,理解为什么要用生成器,它到底为我们的代码带来了怎样的改变,再联想下列表推导式,他们有什么异曲同工之妙,多练代码,多写代码,多百度。