摘要:協(xié)程的基本行為協(xié)程包含四種狀態(tài)等待開始執(zhí)行。協(xié)程中重要的兩個(gè)方法調(diào)用方把數(shù)據(jù)提供給協(xié)程。注意使用調(diào)用協(xié)程時(shí)會(huì)自動(dòng)預(yù)激,因此與裝飾器不兼容標(biāo)準(zhǔn)庫中的裝飾器不會(huì)預(yù)激協(xié)程,因此能兼容句法。因此,終止協(xié)程的本質(zhì)在于向協(xié)程發(fā)送其無法處理的異常。
導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之控制流程篇的重點(diǎn)知識(shí)及個(gè)人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。
本文重點(diǎn):
1、掌握協(xié)程的概念與行為;一、協(xié)程介紹 1、協(xié)程概述
2、掌握協(xié)程中的預(yù)激,終止和異常處理;
3、深入理解yield from的本質(zhì)作用。
協(xié)程:指的是與調(diào)用方協(xié)作,產(chǎn)出由調(diào)用方提供的值。
語法結(jié)構(gòu):協(xié)程是定義體中包含yield關(guān)鍵字的函數(shù),一般使用生成器函數(shù)定義。
意義:協(xié)程中的yield關(guān)鍵字是一種控制流程工具。即不管數(shù)據(jù)如何流動(dòng),協(xié)程都會(huì)把控制權(quán)讓步給中心調(diào)度程序,從而激活其他的協(xié)程實(shí)現(xiàn)協(xié)作式多任務(wù)。
協(xié)程包含四種狀態(tài):
GEN_CREATED:等待開始執(zhí)行。
GEN_RUNNING:解釋器正在執(zhí)行。
GEN_SUSPENDED:在yield表達(dá)式處暫停。
GEN_CLOSED:執(zhí)行結(jié)束。
可使用inspect.getgeneratorstate(...)查詢協(xié)程所處的狀態(tài)。
協(xié)程中重要的兩個(gè)方法:
.send(datum):調(diào)用方把數(shù)據(jù)提供給協(xié)程。
next(coroutine):預(yù)激協(xié)程。
協(xié)程返回值:自Python3.3實(shí)現(xiàn)PEP 380以來對(duì)生成器函數(shù)做了兩處改動(dòng),一處是生成器可以返回值。
3、實(shí)例1:協(xié)程初級(jí)使用——計(jì)算平均值下面將利用協(xié)程計(jì)算用戶傳入若干數(shù)值的平均值。
def average(): total=0.0 number=0 average=None while True: term=yield average total+=term number+=1 average=total/number print(average) process=average() next(process)#預(yù)激協(xié)程 process.send(5)#輸出5 process.send(10)#輸出7.5 process.send(15) #輸出10.0
小結(jié):協(xié)程執(zhí)行首先需要預(yù)激,使之準(zhǔn)備好然后讓步控制權(quán)。具體地說,協(xié)程在yield關(guān)鍵字所在的位置暫停執(zhí)行。在term=yield average這個(gè) 賦值語句中,右邊的代碼會(huì)在賦值之前執(zhí)行。 在暫停結(jié)束后,從先前阻塞的那行代碼開始,將yield 表達(dá)式的值賦給左邊的變量。
實(shí)例2:令協(xié)程返回值
from collections import namedtuple Result = namedtuple("result","average count") def average(): total = 0.0 number = 0 average = None while True: term = yield if term is None: break total += term number += 1 average=total/number return Result(average,number)
分析:當(dāng)協(xié)程終止時(shí),可以在return表達(dá)式中返回值。并且return表達(dá)式通過把值綁定到StopIteration的value屬性上傳給調(diào)用方返回值。事實(shí)上這也符合生成器的常規(guī)行為——耗盡時(shí)拋出StopIteration異常。
二、協(xié)程的預(yù)激、終止與異常處理 1、預(yù)激協(xié)程協(xié)程在使用前須預(yù)激,讓協(xié)程向前執(zhí)行到第一個(gè)yield表達(dá)式,準(zhǔn)備好作為活躍的協(xié)程使用。
預(yù)激的本質(zhì)方法:
next(coroutine):常見的標(biāo)準(zhǔn)方法。
同時(shí)首次發(fā)送coroutine.send(None)也可以調(diào)用next(coroutine),實(shí)現(xiàn)相同功能,但缺乏可讀性。
基于本質(zhì)方法,我們衍生出自定義預(yù)激協(xié)程的裝飾器的方法,避免忘記預(yù)激協(xié)程。
coroutine:預(yù)激協(xié)程的裝飾器
from functools import wraps def coroutine(func): @wraps(func)#把func相關(guān)屬性復(fù)制過來 def manage(*args,**kwargs): gen=func(*args,**kwargs)#獲取生成器對(duì)象 next(gen)#預(yù)激協(xié)程 return gen#返回協(xié)程 return manage
只需將@coroutine語法糖加在生成器函數(shù)上,就可以通過構(gòu)造生成器對(duì)象獲取活躍的協(xié)程。
注意:
使用yield from調(diào)用協(xié)程時(shí)會(huì)自動(dòng)預(yù)激,因此與@coroutine裝飾器不兼容;
Python3.4標(biāo)準(zhǔn)庫中的asyncio.coroutine裝飾器不會(huì)預(yù)激協(xié)程,因此能兼容yield from句法。
2、終止協(xié)程協(xié)程中未處理的異常會(huì)向上冒泡,傳給next函數(shù)或send方法的調(diào)用方。
因此,終止協(xié)程的本質(zhì)在于向協(xié)程發(fā)送其無法處理的異常。下面介紹三種方法終止協(xié)程:
發(fā)送哨符值。常用None和Ellipsis,甚至StopIteration類也可以發(fā)送。
generator.throw(exc_type[,exc_value[,traceback]])
令生成器在暫停的yield表達(dá)式處拋出指定的異常。若生成器處理了此異常,則生成器向前執(zhí)行到下一個(gè)yield表達(dá)式,而產(chǎn)出的值會(huì)成為調(diào)用generator.throw得到的返回值。否則,異常會(huì)向上冒泡,傳到調(diào)用方的上下文中。
generator.close()
令生成器在暫停的yield表達(dá)式處拋出GeneratorExit異常。如果生成器不處理此異常,或者跑出來StopIteration,調(diào)用方不會(huì)報(bào)錯(cuò)。如果收到GeneratorExit異常,生成器一定不能產(chǎn)出值,否則解釋器會(huì)拋出RuntimeError異常。
后兩種方法是自Python2.5開始顯式發(fā)送異常的兩個(gè)方法,建議使用后兩種方法來終止協(xié)程。
3、處理異常在使用協(xié)程的過程中會(huì)產(chǎn)生一些需要處理的異常,此時(shí)可利用try/except處理。如果不管協(xié)程如何結(jié)束都要做一些清理工作,請(qǐng)使用try/finally處理。
實(shí)例1:使用try/finally在協(xié)程終止時(shí)執(zhí)行操作
class DemoException(Exception): """為這次演示定義的異常類型。 """ def demo_finally(): print("-> coroutine started") try: while True: try: x = yield except DemoException: print("*** DemoException handled. Continuing...") else: print("-> coroutine received: {!r}".format(x)) finally: print("-> coroutine ending")三、yield from結(jié)構(gòu) 1、yield from結(jié)構(gòu)介紹
作用介紹:
本質(zhì)作用是打開雙向通道,把最外層的調(diào)用方與最內(nèi)層的子生成器連接起來,這樣兩者可以直接發(fā)送和產(chǎn)出值,還可以直接傳入異常。
替代產(chǎn)出值的嵌套for循環(huán)。
執(zhí)行機(jī)制:
(1)在生成器gen中使用yield from subgen()時(shí),subgen會(huì)獲得控制權(quán),把產(chǎn)出的值傳給gen的調(diào)用方,即調(diào)用方可以直接控制subgen。與此同時(shí),gen會(huì)阻塞,等待subgen終止。
(2)yield from結(jié)構(gòu)會(huì)在內(nèi)部自動(dòng)捕獲StopIteration異常,還會(huì)把對(duì)應(yīng)的value屬性值變成yield from表達(dá)式的值。
實(shí)例1:對(duì)yield from架構(gòu)雙向通道本質(zhì)的深入理解
下面我們結(jié)合實(shí)例深入理解yield from結(jié)構(gòu)。假設(shè)我們需要利用yield from分別計(jì)算一個(gè)班級(jí)男女生身高和體重的平均值,并予以輸出。采用“外部調(diào)用方+委派生成器+子生成器”的結(jié)構(gòu)進(jìn)行設(shè)計(jì),結(jié)構(gòu)示意圖如下:
實(shí)例代碼:
from collections import namedtuple Result=namedtuple("Result","average number") def subaverager():#子生成器經(jīng)委派生成器處理外部數(shù)據(jù),并將值返回給委派生成器。 total = 0.0 number = 0 average = None while True: term = yield if term is None:#外部調(diào)用方控制子生成器終止的關(guān)鍵語句。 break total += term number += 1 average=total/number return Result(average,number) def averager(results,key):#委托生成器架構(gòu)雙向通道。 while True:#避免StopIteration。當(dāng)?shù)玫阶由善鞯姆祷刂禃r(shí),程序會(huì)執(zhí)行到下一個(gè)yield。 results[key]=yield from subaverager() def main(grouper): results={} for key,group in grouper.items(): term = averager(results,key)#構(gòu)建生成器對(duì)象。 next(term) for value in group: term.send(value) term.send(None)#外部調(diào)用方控制子生成器終止的語句。 print(results) result(results) def result(results):#格式化輸出協(xié)程返回的處理數(shù)據(jù)。 for key,value in results.items(): gender,unit=key.split(";") print("{} {} averaging {:.2f} {}.".format( value.number,gender,value.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)
思路擴(kuò)展:上例展示的結(jié)構(gòu)中僅有一個(gè)委派生成器和一個(gè)子生成器。事實(shí)上,這種調(diào)用關(guān)系可以擴(kuò)展到更多的委托生成器上。即把多個(gè)委派生成器連接到一起。一個(gè)委派生成器調(diào)用另一個(gè)子生成器,這個(gè)子生成器本身也是委派生成器。這種鏈?zhǔn)浇Y(jié)構(gòu)最終以一個(gè)只使用yield的簡(jiǎn)單生成器結(jié)束,或者任何的可迭代對(duì)象結(jié)束。
實(shí)例2:替代產(chǎn)出值的嵌套for循環(huán)
def gen(): for c in "AB": yield c for i in range(1, 3): yield i print(list(gen()))#輸出["A", "B", 1, 2]
可以簡(jiǎn)化成:
def gen(): yield from "AB" yield from range(1, 3) print(list(gen()))#輸出["A", "B", 1, 2]3、PEP380中總結(jié)的yield from的六點(diǎn)行為
子生成器產(chǎn)出的值都直接傳給委派生成器的調(diào)用方。
使用send()方法發(fā)給委派生成器的值都直接傳給子生成器。如果發(fā)送None會(huì)調(diào)用生成器的__next__()方法。如果發(fā)送的不是None,那么委派生成器恢復(fù)運(yùn)行。任何其他異常都會(huì)向上冒泡,傳給委派生成器。
生成器退出時(shí),生成器中的return表達(dá)式會(huì)觸發(fā)StopIteration(expr)異常拋出。
yield from表達(dá)式的值是子生成器終止時(shí)傳給StopIteration異常的第一個(gè)參數(shù)。
傳入委派生成器的異常,除了GeneratorExit之外都傳給子生成器的throw()方法。如果調(diào)用throw()方法時(shí)拋出StopIteration異常,委派生成器恢復(fù)運(yùn)行。StopIteration之外的異常會(huì)向上冒泡,傳給委派生成器。
如果把 GeneratorExit 異常傳入委派生成器, 或者在委派生成器上調(diào)用 close() 方法, 那么在子生成器上調(diào)用 close() 方法, 如果它有的話。 如果調(diào)用 close() 方法導(dǎo)致異常拋出, 那么異常會(huì)向上冒泡, 傳給委派生成器; 否則, 委派生成器拋出GeneratorExit 異常。
四、協(xié)程與生成器異同生成器用于生成供迭代的數(shù)據(jù)。
協(xié)程是數(shù)據(jù)的消費(fèi)者,能完成協(xié)作式多任務(wù)活動(dòng)。
協(xié)程與迭代無關(guān)。盡管在協(xié)程中會(huì)使用 yield 產(chǎn)出值, 但這與迭代無關(guān)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/44636.html
摘要:并發(fā)用于制定方案,用來解決可能但未必并行的問題。在協(xié)程中使用需要注意兩點(diǎn)使用鏈接的多個(gè)協(xié)程最終必須由不是協(xié)程的調(diào)用方驅(qū)動(dòng),調(diào)用方顯式或隱式在最外層委派生成器上調(diào)用函數(shù)或方法。對(duì)象可以取消取消后會(huì)在協(xié)程當(dāng)前暫停的處拋出異常。 導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之控制流程篇的重點(diǎn)知識(shí)及個(gè)人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、了解asyncio...
摘要:譯者說于年月日發(fā)布,該版本正式支持的關(guān)鍵字,并且用舊版本編譯同樣可以使用這兩個(gè)關(guān)鍵字,這無疑是一種進(jìn)步。其次,這是最后一個(gè)支持和的版本了,在后續(xù)的版本了會(huì)移除對(duì)它們的兼容。 譯者說 Tornado 4.3于2015年11月6日發(fā)布,該版本正式支持Python3.5的async/await關(guān)鍵字,并且用舊版本CPython編譯Tornado同樣可以使用這兩個(gè)關(guān)鍵字,這無疑是一種進(jìn)步。其次...
摘要:特別是最火的協(xié)程框架也無法保存狀態(tài),讓人非常惋惜。但是因?yàn)闂5谋旧頍o法持久化,所以也就無法持久化。其難度在于,假設(shè)整個(gè)要持久化的調(diào)用棧全部都是內(nèi)的,比如純的。采取的是暴力地把整個(gè)棧區(qū)域拷貝到上的方式來保存其狀態(tài)。 python主流的協(xié)程實(shí)現(xiàn)有五種: cPython的generator cPython的greenlet cPython的fibers stackless python ...
閱讀 974·2021-11-24 10:42
閱讀 3522·2021-11-19 11:34
閱讀 2659·2021-09-29 09:35
閱讀 2543·2021-09-09 09:33
閱讀 688·2021-07-26 23:38
閱讀 2532·2019-08-30 10:48
閱讀 1399·2019-08-28 18:07
閱讀 433·2019-08-26 13:44