python之生成器簡介

一。生成器與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()#關閉後不能傳值
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章