若函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即是生成器对象。
def my_range(start, end, step=1):
print("start....")
while start < end:
yield start
start += step
print("end....")
g = my_range(0, 3)
print(g) #
生成器有内置的__iter__和__next__方法,所以生成器本身是一个迭代器。
因而可以用next()方法来触发生成器所对应函数的执行。
print(next(g)) # 触发函数执行知道遇到yield则停止,将yield后的值返回,并在当前位置挂起函数
# 输出:
# start....
# 0
print(next(g)) # 再次调用next(g),函数从上次暂停的位置继续执行,直到重新遇到yield...
# 输出
# 1
print(next(g)) # 周而复始
# 输出
# 2
print(next(g)) # 触发函数执行没有遇到yield则无值返回,即取值完毕抛出异常结束迭代
# 输出
# end....
# 抛出异常 StopIteration
生成器即然是迭代器,那么可以用for循坏迭代
for i in g:
print(i)
输出结果:
start....
0
1
2
end....
有了yield关键字,就有了一种自定义迭代器的实现方式。yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值。
在函数内可以采用表达式形式的yield
def eater():
print("Ready to eat")
while True:
food = yield
print(f'get the food: {food}, and start to eat')
可以拿到函数的生成器对象持续为函数体send值,如下:
g = eater() # 得到生成器对象
next(g) # 先初始化一次,让函数挂起在food=yield,等待调用g.send()方法为其传值
# 输出:
Ready to eat
g.send("noodle") # 传值,yield='noodle', 即food='noodle',并继续执行函数,直到重新遇到yield,挂起函数
# 输出:
get the food: noodle, and start to eat
g.send("包子") # 传值 yield='包子', 即food='包子',并继续执行函数,直到重新遇到yield,挂起函数
# 输出:
get the food: 包子, and start to eat
针对表达式形式的yield,生成器对象必须事先被初始化一次,让函数挂起在food=yield的位置,等待调用g.send()方法为函数体传值,g.send(None)等同于next(g)。
可以使用装饰器来完成为所有表达式形式的yield对应生成器的初始化操作:
def outer(func):
def inner(*args, **kwargs):
g = func(*args, **kwargs)
next(g)
return g
return inner
@outer
def eater():
print("Ready to eat")
while True:
food = yield
print(f'get the food: {food}, and start to eat')
g = eater()
# Ready to eat
print(g)
#
g.send("baozi")
# get the food: baozi, and start to eat
表达式形式的yield也可以用来返回多次值,即变量名=yield 值的形式,
def eater():
print("Ready to eat")
food_list = []
while True:
food = yield food_list
print(f'get the food: {food}, and start to eat')
food_list.append(food)
g = eater() # 得到生成器对象
res1 = next(g) # 初始化,让函数挂在food = yield food_list处, yield会将后面的值返回,用res1例接收yield的返回值
# Ready to eat
print(res1)
# []
res2 = g.send("baozi") # 传值,yield='baozi', 即food='包子',并继续执行函数,直到重新遇到yield,挂起函数,将yield后面的值返回
# get the food: baozi, and start to eat
print(res2)
# ['baozi']
res3 = g.send("noodle") # 周而复始
# get the food: noodle, and start to eat
print(res3)
# ['baozi', 'noodle']