摘要:當(dāng)前狀態(tài)可以使用函數(shù)確定,該函數(shù)會(huì)返回下述字符串中的一個(gè)。解釋器正在執(zhí)行。打印消息,然后協(xié)程終止,導(dǎo)致生成器對(duì)象拋出異常。實(shí)例運(yùn)行完畢后,返回的值綁定到上。
協(xié)程
協(xié)程可以身處四個(gè)狀態(tài)中的一個(gè)。
當(dāng)前狀態(tài)可以使用inspect.getgeneratorstate(...) 函數(shù)確定,該函數(shù)會(huì)返回下述字符串中的一個(gè)。
"GEN_CREATED"
等待開始執(zhí)行。
"GEN_RUNNING"
解釋器正在執(zhí)行。
"GEN_SUSPENDED"
在 yield 表達(dá)式處暫停。
"GEN_CLOSED"
執(zhí)行結(jié)束。
def simple_coro2(a): print("-> Started: a =", a) b = yield a #等著賦值b 把a(bǔ)甩出去 print("-> Received: b =", b) c = yield a + b print("-> Received: c =", c) my_coro_2 = simple_coro2(14) from inspect import getgeneratorstate print(getgeneratorstate(my_coro_2)) print(next(my_coro_2)) getgeneratorstate(my_coro_2) print(my_coro_2.send(28)) # 沒有yield 出來 所以沒有返回值 print(my_coro_2.send(99)) getgeneratorstate(my_coro_2)
getgeneratorstate 函數(shù)指明,處于 GEN_SUSPENDED 狀態(tài)(即協(xié)程在 yield 表達(dá)式處暫停)。
? 把數(shù)字 99 發(fā)給暫停的協(xié)程;計(jì)算 yield 表達(dá)式,得到 99,然后把那個(gè)數(shù)綁定給 c。
打印 -> Received: c = 99 消息,然后協(xié)程終止,導(dǎo)致生成器對(duì)象拋出
StopIteration 異常。
def averager(): total = 0.0 count = 0 average = None while True: term = yield average total += term count += 1 average = total / count coro_avg = averager() print(next(coro_avg)) print(coro_avg.send(10)) print(coro_avg.send(15)) print(coro_avg.send(20))
調(diào)用 next 函數(shù),預(yù)激協(xié)程。
? 這個(gè)無限循環(huán)表明,只要調(diào)用方不斷把值發(fā)給這個(gè)協(xié)程,它就會(huì)一直接收值,然后生
成結(jié)果。僅當(dāng)調(diào)用方在協(xié)程上調(diào)用 .close() 方法,或者沒有對(duì)協(xié)程的引用而被垃圾回收
程序回收時(shí),這個(gè)協(xié)程才會(huì)終止。
? 這里的 yield 表達(dá)式用于暫停執(zhí)行協(xié)程,把結(jié)果發(fā)給調(diào)用方;還用于接收調(diào)用方后面
發(fā)給協(xié)程的值,恢復(fù)無限循環(huán)。
發(fā)送某個(gè)哨符值,讓協(xié)程退出。
內(nèi)置的 None 和Ellipsis 等常量經(jīng)常用作哨符值。
Ellipsis 的優(yōu)點(diǎn)是,數(shù)據(jù)流中不太常有這個(gè)值。
throwgenerator.throw(exc_type[, exc_value[, traceback]])
致使生成器在暫停的 yield 表達(dá)式處拋出指定的異常。
如果生成器處理了拋出的異常,代碼會(huì)向前執(zhí)行到下一個(gè) yield 表達(dá)式,而產(chǎn)出的值會(huì)成為調(diào)用 generator.throw方法得到的返回值。
generator.close()致使生成器在暫停的 yield 表達(dá)式處拋出 GeneratorExit 異常。
如果生成器沒有處理這個(gè)異常,或者拋出了 StopIteration 異常(通常是指運(yùn)行到結(jié)尾),調(diào)用方不會(huì)報(bào)錯(cuò)。
如果收到 GeneratorExit 異常,生成器一定不能產(chǎn)出值,否則解釋器會(huì)拋出
RuntimeError 異常。
exc_coro.throw(ZeroDivisionError)
exc_coro.close()
from collections import namedtuple Result = namedtuple("Result", "count average") def averager(): total = 0.0 count = 0 average = None while True: term = yield if term is None: break total += term count += 1 average = total / count return Result(count, average) coro_avg = averager() next(coro_avg) coro_avg.send(30) coro_avg.send(6.5) try: coro_avg.send(None) except StopIteration as exc: result = exc.value print(result)
捕獲 StopIteration 異常,獲取 averager 返回的值
yield from 結(jié)構(gòu)會(huì)在內(nèi)部自動(dòng)捕獲 StopIteration 異常。使用yield from
這種處理方式與 for 循環(huán)處理 StopIteration 異常的方式一樣:循環(huán)機(jī)制使用用戶易于理解的方式處理異常。
對(duì) yield from 結(jié)構(gòu)來說,解釋器不僅會(huì)捕獲 StopIteration 異常,還會(huì)把value 屬性的值變成 yield from 表達(dá)式的值。
yield from 結(jié)構(gòu)唯一的作用是替代產(chǎn)出值的嵌套 for 循環(huán), 這句話不對(duì)
yield from 的主要功能是打開雙向通道,把最外層的調(diào)用方與最內(nèi)層的子生成器連接起來,
這樣二者可以直接發(fā)送和產(chǎn)出值,還可以直接傳入異常,
而不用在位于中間的協(xié)程中添加大量處理異常的樣板代碼。
有了這個(gè)結(jié)構(gòu),協(xié)程可以通過以前不可能的方式委托職責(zé)。
案例委派生成器
包含 yield from
子生成器
從 yield from 表達(dá)式中
(“Syntax for Delegating to a Subgenerator”)中所說的“子生成器”(subgenerator)。
調(diào)用方
PEP 380 使用“調(diào)用方”這個(gè)術(shù)語指代調(diào)用委派生成器的客戶端代碼。在不同的語境
中,我會(huì)使用“客戶端”代替“調(diào)用方”,以此與委派生成器(也是調(diào)用方,因?yàn)樗{(diào)用了子
生成器)區(qū)分開。
不使用yield from
from collections import namedtuple Result = namedtuple("Result", "count average") # 子生成器 def averager(): # ? total = 0.0 count = 0 average = None while True: term = yield # ? if term is None: # ? break total += term count += 1 average = total / count return Result(count, average) # ? data = { "girls;kg": [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5], "girls;m": [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43], "boys;kg": [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], "boys;m": [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46], } def main(data): result = {} for key, values in data.items(): coro_avg = averager() next(coro_avg) for value in values: coro_avg.send(value) try: coro_avg.send(None) except StopIteration as exc: result[key] = exc.value print(result) if __name__ == "__main__": main(data)
這里的try: catch stop異常要一直存在
用yiled from 及委派生成器作用
from collections import namedtuple Result = namedtuple("Result", "count average") # 子生成器 def averager(): # ? total = 0.0 count = 0 average = None while True: term = yield # ? if term is None: # ? break total += term count += 1 average = total / count return Result(count, average) # ? data = { "girls;kg": [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5], "girls;m": [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43], "boys;kg": [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], "boys;m": [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46], } def grouper(results): while True: res_obj = yield from averager() results.append(res_obj) def main(data): results = [] for key, values in data.items(): coro_avg = grouper(results) next(coro_avg) for value in values: coro_avg.send(value) # 這個(gè)None是停止返回 哨兵 coro_avg.send(None) print(results) # report(result) # 輸出報(bào)告 def report(results): for key, result in sorted(results.items()): group, unit = key.split(";") print("{:2} {:5} averaging {:.2f}{}".format( result.count, group, result.average, unit)) if __name__ == "__main__": main(data)
官方的案例
from collections import namedtuple Result = namedtuple("Result", "count average") # 子生成器 def averager(): # ? total = 0.0 count = 0 average = None while True: term = yield # ? if term is None: # ? break total += term count += 1 average = total / count return Result(count, average) # ? # 委派生成器 def grouper(results, key): # ? while True: # ? results[key] = yield from averager() # ? # 客戶端代碼,即調(diào)用方 def main(data): # ? results = {} for key, values in data.items(): group = grouper(results, key) # ? next(group) # ? for value in values: group.send(value) # ? group.send(None) # 重要! #? print(results) # 如果要調(diào)試,去掉注釋 # report(results) # 輸出報(bào)告 def report(results): for key, result in sorted(results.items()): group, unit = key.split(";") print("{:2} {:5} averaging {:.2f}{}".format( result.count, group, result.average, unit)) data = { "girls;kg": [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5], "girls;m": [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43], "boys;kg": [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], "boys;m": [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46], } if __name__ == "__main__": main(data)
? 至關(guān)重要的終止條件。如果不這么做,使用 yield from 調(diào)用這個(gè)協(xié)程的生成器會(huì)永
遠(yuǎn)阻塞。
? 返回的 Result 會(huì)成為 grouper 函數(shù)中 yield from 表達(dá)式的值
? 這個(gè)循環(huán)每次迭代時(shí)會(huì)新建一個(gè) averager 實(shí)例;每個(gè)實(shí)例都是作為協(xié)程使用的生成
器對(duì)象。
? grouper 發(fā)送的每個(gè)值都會(huì)經(jīng)由 yield from 處理,通過管道傳給 averager 實(shí)
例。grouper 會(huì)在 yield from 表達(dá)式處暫停,等待 averager 實(shí)例處理客戶端發(fā)來的
值。averager 實(shí)例運(yùn)行完畢后,返回的值綁定到 results[key] 上。while 循環(huán)會(huì)不斷
創(chuàng)建 averager 實(shí)例,處理更多的值。
yield from 結(jié)構(gòu)會(huì)在內(nèi)部自動(dòng)捕獲 StopIteration 異常。這種處理方總結(jié)
式與 for 循環(huán)處理 StopIteration 異常的方式一樣:循環(huán)機(jī)制使用用戶易于理解的方式
處理異常。對(duì) yield from 結(jié)構(gòu)來說,解釋器不僅會(huì)捕獲 StopIteration 異常,還會(huì)把
value 屬性的值變成 yield from 表達(dá)式的值。
這里先不總結(jié) 帶我把協(xié)程 程序加進(jìn)去
如何使用協(xié)程在單個(gè)線程中管理并發(fā)活動(dòng)。文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/42066.html
摘要:在復(fù)雜的情況下,需要具體策略維護(hù)內(nèi)部狀態(tài)時(shí),可能需要把策略和享元模式結(jié)合起來。函數(shù)比用戶定義的類的實(shí)例輕量,而且無需使用享元模式,因?yàn)楦鱾€(gè)策略函數(shù)在編譯模塊時(shí)只會(huì)創(chuàng)建一次。 一等函數(shù)實(shí)現(xiàn)設(shè)計(jì)模式 經(jīng)典的策略模式定義 定義一系列算法,把它們一一封裝起來,并且使它們可以相互替換。本模式使得算法可以獨(dú)立于使用它的客戶而變化。 案例 假如一個(gè)網(wǎng)店制定了下述折扣規(guī)則。 有 1000 或以上積分...
摘要:于此同時(shí),會(huì)阻塞,等待終止。子生成器返回之后,解釋器會(huì)拋出異常,并把返回值附加到異常對(duì)象上,只是委派生成器恢復(fù)。實(shí)例運(yùn)行完畢后,返回的值綁定到上。這一部分處理調(diào)用方通過方法傳入的異常。之外的異常會(huì)向上冒泡。 上一篇python協(xié)程1:yield的使用介紹了: 生成器作為協(xié)程使用時(shí)的行為和狀態(tài) 使用裝飾器預(yù)激協(xié)程 調(diào)用方如何使用生成器對(duì)象的 .throw(...) 和 .close()...
摘要:協(xié)程定義協(xié)程的底層架構(gòu)是在中定義,并在實(shí)現(xiàn)的。為了簡化,我們會(huì)使用裝飾器預(yù)激協(xié)程。執(zhí)行上述代碼結(jié)果如下出錯(cuò)的原因是發(fā)送給協(xié)程的值不能加到變量上。示例使用和方法控制協(xié)程。 最近找到一本python好書《流暢的python》,是到現(xiàn)在為止看到的對(duì)python高級(jí)特性講述最詳細(xì)的一本。看了協(xié)程一章,做個(gè)讀書筆記,加深印象。 協(xié)程定義 協(xié)程的底層架構(gòu)是在pep342 中定義,并在python2...
摘要:可迭代的對(duì)象迭代器和生成器理念迭代是數(shù)據(jù)處理的基石。可迭代的對(duì)象與迭代器的對(duì)比從可迭代的對(duì)象中獲取迭代器標(biāo)準(zhǔn)的迭代器接口有兩個(gè)方法。此外,也沒有辦法還原迭代器。最終,函數(shù)的定義體返回時(shí),外層的生成器對(duì)象會(huì)拋出異常這一點(diǎn)與迭代器協(xié)議一致。 可迭代的對(duì)象、迭代器和生成器 理念 迭代是數(shù)據(jù)處理的基石。掃描內(nèi)存中放不下的數(shù)據(jù)集時(shí),我們要找到一種惰性獲取數(shù)據(jù)項(xiàng)的方式,即按需一次獲取一個(gè)數(shù)據(jù)項(xiàng)。這...
閱讀 1198·2021-11-10 11:35
閱讀 2925·2021-09-24 10:35
閱讀 2957·2021-09-22 15:38
閱讀 2807·2019-08-30 15:43
閱讀 1338·2019-08-29 18:39
閱讀 2558·2019-08-29 15:22
閱讀 2789·2019-08-28 18:17
閱讀 612·2019-08-26 13:37