一。生成器与yield
若函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即生成器对象
>>> def my_range(start,stop,step=1):
... print('start...')
... while start < stop:
... yield start
... start+=step
... print('end...')
...
>>> g=my_range(0,3)
>>> g
<generator object my_range at 0x104105678>
生成器内置有__iter__和__next__方法,所以生成器本身就是一个迭代器
>>> g.__iter__
<method-wrapper '__iter__' of generator object at 0x1037d2af0>
>>> g.__next__
<method-wrapper '__next__' of generator object at 0x1037d2af0>
因而我们可以用next(生成器)出发生成器所对应函数的执行
>>> next(g) # 触发函数执行直到遇到yield则停止,将yield后的值返回,并在当前位置挂起函数
start...
0
>>> next(g) # 再次调用next(g),函数从上次暂停的位置继续执行,直到重新遇到yield...
1
>>> next(g) # 周而复始...
2
>>> next(g) # 触发函数执行没有遇到yield则无值返回,即取值完毕抛出异常结束迭代
end...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
既然生成器对象属于迭代器,那么必然可以使用for循环迭代,如下:
>>> for i in countdown(3):
... print(i)
...
countdown start
3
2
1
Done!
有了yield关键字,我们就有了一种自定义迭代器的实现方式。yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值
二yield表达式的应用
在函数内可以采用表达式形式的yield
def eater():
print('ready to eat')
while True:
food=yield
print('get the food: %s,and start to eat' %food)
可以拿到函数的生成器对象持续为函数体send值,如下:
g=eater() #得到生成器对象
print(g)#<generator object eater at 0x0000000001E166D8>
next(g)#ready to eat 需要事先初始化一次,让函数挂起在food=yield,等待调用g.seed()方法为期传值
print(g.send('包子'))#get the food: 包子,and start to eat
print(g.send('鸡腿'))#get the food: 鸡腿,and start to eat
针对表达式形式的yield,生成器对象必须事先被初始化一次,让函数挂起在food=yield的位置,等待调用g.send()方法为函数体传值,g.send(None)等同于next(g)
我们可以编写装饰器来完成为所有表达式形式yield对应生成器的初始化操作,如下:
def init(func):#func=eater的内存地址
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return wrapper
@init # eater=init(eater)====>eater=wrapper的内存地址
def eater():
print('ready to eat')
while True:
food=yield
print('get the food %d,and start to eat' %food)
eater()# 此时此处的eater已被装饰器偷换成wrapper
#执行开始
#wrapper()
#g = func(*args, **kwargs)#func=eater的内存地址
#print('ready to eat')
#food=yield
#执行到这里因为遇到了yield,所以暂停了
表达式形式的yield也可以用于返回多次值,即变量名=yield 值的形式,如下:
def eater():
print('ready to eat')
food_list=[]
while True:
food=yield food_list
food_list.append(food)
print(food_list)
e=eater()
print(next(e))
e.send('卫龙')
e.send('烧麦')
e.close()#关闭后不能传值