參悟yield 和yield from (加精)

 好久沒有複習協程知識了,翻看文檔感受像個小學生同樣,參悟了幾個小時總算是透了,大佬們莫要見笑。

 

yield用法:

  next(itr)至關於 itr.send(None),兩種方式都會觸發生成器運行

  下面 代碼 # 1處 運行至 yield index 的第一次斷點 , #2  對jump進行賦值 並運行至第二個斷點處 ...... 

def jumping_range(N):
    index = 0
    while index < N:
        # 經過send()發送的信息將賦值給jump
        jump = yield index
        # print("jump", jump)
        if jump is None:
            jump = 1
        index += jump

if __name__ == '__main__':
    itr = jumping_range(5)
    print(next(itr))     # 1
    print(itr.send(2))    # 2
    print(next(itr))     # 3
    print(itr.send(-1))  #4

 

 若是想要完成全部的賦值取值, 最後一次next()會觸發 StopIteration的錯誤 能夠加try捕捉 

例如:

if __name__ == '__main__':
    itr = jumping_range(5)
    print(next(itr))
    print(itr.send(2))
    print(next(itr))
    print(itr.send(-1))
    print(next(itr))
    print(next(itr))
    try:
        print(next(itr))
    except StopIteration as e:
        print(e.value)

 

 

yield from的用法

很少說直接代碼比較 ,下面一個例子函數

  用yield實現spa

# 字符串
astr='ABC'
# 列表
alist=[1,2,3]
# 字典
adict={"name":"wangbm","age":18}
# 生成器
agen=(i for i in range(4,8))

def gen(*args, **kw):
    print(args)
    for item in args:
        for i in item:
            yield i

new_list=gen(astr, alist, adict, agen)
print(next(new_list))
#print(type(new_list))
#['A', 'B', 'C', 1, 2, 3, 'name', 'age', 4, 5, 6, 7]

  使用yield from code

# 字符串
astr='ABC'
# 列表
alist=[1,2,3]
# 字典
adict={"name":"wangbm","age":18}
# 生成器
agen=(i for i in range(4,8))

def gen(*args, **kw):
    for item in args:
        yield from item

new_list=gen(astr, alist, adict, agen)
print(list(new_list))
# ['A', 'B', 'C', 1, 2, 3, 'name', 'age', 4, 5, 6, 7]

由上面兩種方式對比,能夠看出,yield from後面加上可迭代對象,他能夠把可迭代對象裏的每一個元素一個一個的yield出來,對比yield來講代碼更加簡潔,結構更加清晰。orm

 

 

yield from 的深層次應用:協程

  首先得知道一個概念:對象

  

一、調用方:調用委派生成器的客戶端(調用方)代碼
二、委託生成器:包含yield from表達式的生成器函數
三、子生成器:yield from後面加的生成器函數

  

# 子生成器
def average_gen():
    total = 0
    count = 0
    average = 0
    while True:
        new_num = yield average
        count += 1
        total += new_num
        average = total/count

# 委託生成器
def proxy_gen():
    while True:
        yield from average_gen()

# 調用方
def main():
    calc_average = proxy_gen()
    next(calc_average)            # 預激下生成器
    print(calc_average.send(10))  # 打印:10.0
    print(calc_average.send(20))  # 打印:15.0
    print(calc_average.send(30))  # 打印:20.0

if __name__ == '__main__':
    main()

 

委託生成器的做用: 在調用方和子生成器之間行成雙向通道

雙向通道的含義:調用方能夠經過send()直接發送消息給子生成器,而子生成器yield的值,也是直接返回給調用方。

 

注意:

你可能會常常看到有些代碼,還能夠在yield from前面看到能夠賦值。這是什麼用法?blog

你可能會覺得,子生成器yield回來的值,被委託生成器給攔截了。你能夠親自寫個demo運行試驗一下,並非你想的那樣。
由於咱們以前說了,委託生成器,只起一個橋樑做用,它創建的是一個雙向通道,它並無權利也沒有辦法,對子生成器yield回來的內容作攔截。文檔

 

例子:字符串

# 子生成器
def average_gen():
    total = 0
    count = 0
    average = 0
    while True:
        new_num = yield average
        if new_num is None:
            break
        count += 1
        total += new_num
        average = total/count

    # 每一次return,都意味着當前協程結束。
    return total,count,average

# 委託生成器
def proxy_gen():
    while True:
        # 只有子生成器要結束(return)了,yield from左邊的變量纔會被賦值,後面的代碼纔會執行。
        total, count, average = yield from average_gen()
        print("計算完畢!!\n總共傳入 {} 個數值, 總和:{},平均數:{}".format(count, total, average))

# 調用方
def main():
    calc_average = proxy_gen()
    next(calc_average)            # 預激協程
    print(calc_average.send(10))  # 打印:10.0
    print(calc_average.send(20))  # 打印:15.0
    print(calc_average.send(30))  # 打印:20.0
    calc_average.send(None)      # 結束協程
    # 若是此處再調用calc_average.send(10),因爲上一協程已經結束,將重開一協程

if __name__ == '__main__':
    main()

結果:it

10.0
15.0
20.0
計算完畢!!
總共傳入 3 個數值, 總和:60,平均數:20.0

 

淺層次談論yield from的做用,它能夠幫咱們處理異常

相關文章
相關標籤/搜索