yield用法
带yield的函数其实是一个生成器,但为了好理解,不妨先把yield看成一个return,后面的语句暂时不执行
e.g. 1
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
程序开始执行后,因为foo()里面有yield关键字,所以先得到一个生成器g
调用next(),foo()才真正执行,先打印出"starting...",进入while循环
遇到yield,将其看做return 4,程序在此暂停,此时没有将4赋值给res,此时next(g)完成,即print(4)
打印20个*
又开始下面的next(),但这时候是从刚才暂停的地方开始执行,也就是要执行res的赋值操作,这时候要注意,此时res右边是没有值的,因为4
已经return出去了,所以这个时候res赋值是None,将其打印出来之后,程序继续在while循环里执行,此时又碰到了yield,这时候同样return 4,print(4)
小结:带yield的函数时一个生成器,这个生成器有一个next函数,next开始的地方是接着上一次next停止的地方
e.g.2
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
为什么要用生成器
用list会占用很多内存空间,而用yield
def foo(num):
print("starting...")
while num<10:
num += 1
yield num
for n in foo(0):
print(n)
# 也可以单步调试
f = foo(0)
next(f)
next(f)
简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。
结论:
一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。