浅谈一下python yield的使用,最近在程序中很经常遇到这个关键词。在python中带有yield的函数称为生成器。可以把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的作用是将一个函数变成一个生成器,调用fab(5)不会执行fab这个函数,而是返回一个iteration对象。在for循环执行时,每次循环都会执行fab函数内部的代码,执行到yield b时,fab函数就会返回一个迭代值,下次迭代开始时,代码从yield b的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇见yield。
下面使用next()方法观察一下执行流程。
f = fab(5)
next(f)
1
next(f)
1
next(f)
2
next(f)
3
next(f)
5
next(f)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-9-aff1dd02a623> in <module>
----> 1 next(f)
StopIteration:
当函数执行结束,生成器会抛出异常,表示迭代完成。可以使用next()对其进行调用,在for循环中会自动调用next,使用yield就是把一个函数改写为生成器,使其有迭代能力。next就相当于下一步生成哪个数,这一次的next开始的地方就是接着上一次的next停止的地方执行的,所以调用next的时候,生成器不会从函数开始执行,只是接着上一步停止的地方开始,然后遇到yield,就return出要生成的数,此步迭代结束。
下面认识一下生成器的send函数。
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(next(g))
starting...
4
********************
res: None
4
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(g.send(7))
starting...
4
********************
res: 7
4
send函数作用是发送一个参数给res,在每步return的时候,并没有把4赋值给res,下次执行的时候只好继续执行赋值操作,只好赋值为None,但是使用send的话,开始执行的时候,会先接着上一次(return 4)执行,先把7赋值给res,然后执行next的作用。
生成器的用处是什么,想象下在List很大的时候,会很占内存空间,比如获取0,1,2…,1000。
for n in range(1000):
a=n
使用for的方法很占空间,可以用yield进行改写。
def foo(num):
while num<10:
num=num+1
yield num
for n in foo(0):
print(n)
1
2
3
4
5
6
7
8
9
10
但是在正常使用的时候,还是推荐使用range(python2中是xrange),它已经不是简单List了,是一个<class ‘range’>也就是生成器对象,python已经自己优化好了。