一。生成器與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()#關閉後不能傳值