天天看點

Python中生成器函數和yield from

Python生成器函數和yield與from

生成器generator

  • 生成器指的是生成器對象,可以由生成器表達式得到,也可以使用yield關鍵字得到一個生成器函數,調用這個函數得到一個生成器對象
  • 生成器對象,是一個可疊代對象,是一個疊代器
  • 生成器對象,是延遲計算、惰性求值

yield關鍵字

  • yield關鍵字,可以中斷目前函數執行。如果函數中使用了yield 那麼該函數一定是一個生成器函數

生成器函數

  • 定義:函數體中包含了yield關鍵字的函數,被稱作為生成器函數。生成器函數調用後傳回生成器對象。生成器函數體不會在生成器函數調用時立即執行
  • next(generator)可以擷取生成器函數生成的生成器對象的下一個值
  • generator.send(arg)可以擷取生成器函數生成的生成器對象的下一個值。同時,會将arg的值傳遞給需要擷取yield傳回值的對象。
  1. 簡單示例一:
def getNum():
for i in range(5):
    yield i   #注意當執行時遇到yield語句,就暫停該函數的執行。并将yield後面的值傳回。
gnum = getNum() #生成器函數執行,會傳回生成器對象。可疊代。

for i in range(6):
    print(next(gnum)) # 注意,如果函數體執行完成,生成器的遊标走到了末尾。會報StopIteration錯誤      
Python中生成器函數和yield from

2. 簡單示例二(練習使用send與yield結合):

def counter():
def sol():
    n = 0
    while True:
        n += 1
        response = yield n  #使用response擷取關鍵字yield的傳回值。注意使用next()調用時傳回值是None
        if isinstance(response,int):
            n = response
    soll = sol()  #定義一個sol生成器對象
    return lambda x=None: next(soll) if x is None else soll.send(x)

cunt = counter() #執行counter函數,擷取一個操作計數器的函數。
print([cunt() for _ in range(5)])  #根據計數器函數擷取5個值放在list中
print(cunt(100))  #初始化計數器函數的初始值
print([cunt() for _ in range(5)])  #根據計數器函數,擷取5個值放在list中      
Python中生成器函數和yield from

3. 例子3:生成器中的閉包

def getcont():
    key = 5
    hh = (key+i for i in range(4))
    key = 100  #注意此時生成器中的key對應的值會變成100
    return hh
hh = getcont()  ## 注意此時生成器中key對應的值變成100,形成閉包。
key = 120 #此時key與hh中生成器的key不同
list(hh)      
Python中生成器函數和yield from
def add(n,i):
    return n+i

def test():   #生成器函數值為【0,1,2,3,】
    for i in range(4):
        yield i

g=test()
for n in [1,10]:
    g=(add(n,i) for i in g)   #定義生成器,其中生成器中n為辨別符。注意:n為辨別符,當循環執行完成後,局部變量中n的值為10
#     g = (add(n,i) for i in (add(n,i) for i in g) )
print(list(g))      
  • 注意:
  1. 生成器函數中,可以多次使用yield,每次執行一個車yield會暫停執行,把yield表達式的值傳回
  2. 再次執行會執行到下一個 yield語句,又會暫停
  3. return語句依然可以終止函數運作,但return語句的傳回值不能被擷取到
  4. return會結束生成器函數。如果函數體執行完成,生成器的遊标就走到了末尾。無法取下一個值,會報StopIteration錯誤
  5. 如果函數沒有顯式的return語句,生成器函數執行到結尾(相當于執行了return None),一樣會抛出StopIteration異常
  • 總結
  1. 包含yield語句的生成器函數調用後,會生成生成器函數對象。生成器函數的函數體不會立即執行
  2. next(generator)會從函數的目前位置向後執行到之後碰到的第一個yield語句,會彈出yield語句後面的值。并暫停函數執行。
  3. 再次調用next函數,會再次執行函數,如果再次碰到yield,會再次暫停函數,并傳回值。如果沒有碰到yield而碰到了函數的return語句,會抛出topIteration異常

協程Coroutine

  • 生成器的進階用法。它比程序、線程輕量級,是在使用者控件排程函數的一種實作
  • Python3 asyncio就是協程實作,已經加入到标準庫
  • Python3.5 使用async、await關鍵字直接原生支援攜程
  • 協程排程器實作思路
  • 有兩個生成器A、B。next(A)後,A執行到yield語句暫停,然後去執行next(B),B執行到yield語句後也暫停,然後再次調用next(A),再調用next(B),周而複始,就實作了排程的效果。
  • 可以引入排程的政策來實作切換的方式
  • 協程是一種非搶占排程

yield from文法

  • 從Python3.3開始增加了yield from文法.
  • yield from就是一種簡化的文法糖
  • yield from iterable 等價于 for item in iterable : yield item
  • 簡單示例
def sol():
    for i in [1,2,3]: yield i

def sol2():  #注意:sol2與sol效果等價
    yield from [1,2,3]
iterab = sol()
iterab2 = sol2()
print(next(iterab),next(iterab))
print(next(iterab2),next(iterab2))