> 文章列表 > Python基础之生成器

Python基础之生成器

Python基础之生成器

一、生成器与yield

函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即是生成器对象。

def my_range(start, end, step=1):print("start....")while start < end:yield startstart += stepprint("end....")g = my_range(0, 3)
print(g)     # <generator object my_range at 0x104f1b3e0>

生成器有内置的__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表达式应用

在函数内可以采用表达式形式的yield

def eater():print("Ready to eat")while True:food = yieldprint(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 greturn inner@outer
def eater():print("Ready to eat")while True:food = yieldprint(f'get the food: {food}, and start to eat')g = eater()
# Ready to eat
print(g)
# <generator object eater at 0x104bf8040>
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_listprint(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']